Medium

题目描述

给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目

如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则数组 a 是数组 b 的一个 子集 。如果选中的元素下标不同,则认为两个子集 不同

对数组 a 执行 按位或,结果等于 a[0] OR a[1] OR ... OR a[a.length - 1](下标从 0 开始)。

示例 1:

输入:nums = [3,1]
输出:2
解释:子集按位或可能的最大值是 3 。有 2 个子集按位或值为 3 :
- [3]
- [3,1]

示例 2:

输入:nums = [2,2,2]
输出:7
解释:[2,2,2] 的所有非空子集的按位或值都是 2 。总共有 2^3 - 1 = 7 个子集。

示例 3:

输入:nums = [3,2,1,5]
输出:6
解释:子集按位或可能的最大值是 7 。有 6 个子集按位或值为 7 :
- [3,5]
- [3,1,5]  
- [3,2,5]
- [3,2,1,5]
- [2,5]
- [2,1,5]

提示:

  • 1 <= nums.length <= 16
  • 1 <= nums[i] <= 10^5

解题思路

这是一道关于子集枚举和位运算的题目。

核心思路:

  1. 首先需要找到数组所有元素的按位或的最大值
  2. 然后统计有多少个子集的按位或值等于这个最大值

关键观察:

  • 按位或运算具有单调性:添加更多元素只会让结果变大或保持不变
  • 因此最大的按位或值就是所有元素的按位或
  • 数组长度最大为16,所以可以用枚举所有子集的方法

解法1:位运算枚举(推荐) 使用位掩码枚举所有可能的子集。对于长度为n的数组,共有2^n-1个非空子集,用1到2^n-1的整数的二进制表示来枚举每个子集。

解法2:回溯法 使用递归回溯遍历所有子集,对每个子集计算按位或值。

由于数据规模较小(n≤16),两种方法的时间复杂度都是可接受的。位运算枚举更简洁直观,是推荐解法。

算法步骤:

  1. 计算所有元素的按位或作为最大值
  2. 枚举所有非空子集,计算每个子集的按位或
  3. 统计按位或值等于最大值的子集数量

代码实现

class Solution {
public:
    int countMaxOrSubsets(vector<int>& nums) {
        int maxOr = 0;
        for (int num : nums) {
            maxOr |= num;
        }
        
        int n = nums.size();
        int count = 0;
        
        for (int mask = 1; mask < (1 << n); mask++) {
            int currentOr = 0;
            for (int i = 0; i < n; i++) {
                if (mask & (1 << i)) {
                    currentOr |= nums[i];
                }
            }
            if (currentOr == maxOr) {
                count++;
            }
        }
        
        return count;
    }
};
class Solution:
    def countMaxOrSubsets(self, nums: List[int]) -> int:
        max_or = 0
        for num in nums:
            max_or |= num
        
        n = len(nums)
        count = 0
        
        for mask in range(1, 1 << n):
            current_or = 0
            for i in range(n):
                if mask & (1 << i):
                    current_or |= nums[i]
            if current_or == max_or:
                count += 1
        
        return count
public class Solution {
    public int CountMaxOrSubsets(int[] nums) {
        int maxOr = 0;
        foreach (int num in nums) {
            maxOr |= num;
        }
        
        int n = nums.Length;
        int count = 0;
        
        for (int mask = 1; mask < (1 << n); mask++) {
            int currentOr = 0;
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0) {
                    currentOr |= nums[i];
                }
            }
            if (currentOr == maxOr) {
                count++;
            }
        }
        
        return count;
    }
}
/**
 * @param {number[]} nums
 * @return {number}
 */
var countMaxOrSubsets = function(nums) {
    let maxOr = 0;
    for (let num of nums) {
        maxOr |= num;
    }
    
    let count = 0;
    
    function backtrack(index, currentOr) {
        if (index === nums.length) {
            if (currentOr === maxOr) {
                count++;
            }
            return;
        }
        
        backtrack(index + 1, currentOr | nums[index]);
        backtrack(index + 1, currentOr);
    }
    
    backtrack(0, 0);
    return count;
};

复杂度分析

复杂度类型说明
时间复杂度O(n × 2^n)枚举2^n个子集,每个子集需要O(n)时间计算按位或
空间复杂度O(1)只使用常数额外空间

相关题目