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 <= 161 <= nums[i] <= 10^5
解题思路
这是一道关于子集枚举和位运算的题目。
核心思路:
- 首先需要找到数组所有元素的按位或的最大值
- 然后统计有多少个子集的按位或值等于这个最大值
关键观察:
- 按位或运算具有单调性:添加更多元素只会让结果变大或保持不变
- 因此最大的按位或值就是所有元素的按位或
- 数组长度最大为16,所以可以用枚举所有子集的方法
解法1:位运算枚举(推荐) 使用位掩码枚举所有可能的子集。对于长度为n的数组,共有2^n-1个非空子集,用1到2^n-1的整数的二进制表示来枚举每个子集。
解法2:回溯法 使用递归回溯遍历所有子集,对每个子集计算按位或值。
由于数据规模较小(n≤16),两种方法的时间复杂度都是可接受的。位运算枚举更简洁直观,是推荐解法。
算法步骤:
- 计算所有元素的按位或作为最大值
- 枚举所有非空子集,计算每个子集的按位或
- 统计按位或值等于最大值的子集数量
代码实现
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) | 只使用常数额外空间 |
相关题目
. Subsets (Medium)
. Largest Combination With Bitwise AND Greater Than Zero (Medium)