给你一个整数数组 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 。总共有 23 - 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] <= 105
法一:通过位运算求出所有子集,然后计算每个子集按位或的结果:
class Solution {
public:
int countMaxOrSubsets(vector<int>& nums) {
int maxOrRes = 0;
int maxOrResNum = 0;
int maxSubsetNum = pow(2, nums.size());
for (int i = 0; i < maxSubsetNum; ++i)
{
int orRes = 0;
for (int j = 0; j < nums.size(); ++j)
{
if ((1 << j) & i)
{
orRes |= nums[j];
}
}
if (orRes > maxOrRes)
{
maxOrRes = orRes;
maxOrResNum = 1;
}
else if (orRes == maxOrRes)
{
++maxOrResNum;
}
}
return maxOrResNum;
}
};
如果nums中有n个元素,此算法时间复杂度为O(n2 n ^n n),空间复杂度为O(1)。
法二:同法一,但通过递归来找所有子集:
class Solution {
public:
int countMaxOrSubsets(vector<int>& nums) {
int ans = 0;
findSubsets(nums, 0, ans, 0);
return ans;
}
private:
int curOrRes = 0;
int curMaxSubsetOrRes = 0;
void findSubsets(const vector<int> &nums,
int index,
int &ans,
int curSubsetOrRes
)
{
if (index == nums.size())
{
if (curSubsetOrRes > curMaxSubsetOrRes)
{
curMaxSubsetOrRes = curSubsetOrRes;
ans = 1;
}
else if (curSubsetOrRes == curMaxSubsetOrRes)
{
++ans;
}
return;
}
findSubsets(nums, index + 1, ans, curSubsetOrRes);
findSubsets(nums, index + 1, ans, curSubsetOrRes | nums[index]);
}
};
如果nums中有n个元素,此算法时间复杂度为O(n2 n ^n n),空间复杂度为O(n)。
法三:类似法一,我们用数字中值为1的位来表示nums中被选中到该数字表示的子集中的数在nums中的下标,但这次我们用动态规划来实现:
class Solution {
public:
int countMaxOrSubsets(vector<int>& nums) {
int subsetNum = 1 << nums.size();
vector<int> dp(subsetNum);
// 处理子集中只有1个数字时的按位或,此时按位或为该数字本身
for (int i = 0; i < nums.size(); ++i)
{
dp[1 << i] = nums[i];
}
int ans = 0;
int max = 0;
for (int i = 1; i < subsetNum; ++i)
{
int lowbit = i & -i;
// dp[i - lowbit]就是去除lowbit后的数字表示的子集
// dp[lowbit]是lowbit表示的子集,显然lowbit只有一位
// 结果就是i - lowbit表示的子集与lowbit表示的子集的与
dp[i] = dp[i - lowbit] | dp[lowbit];
if (dp[i] > max)
{
max = dp[i];
ans = 1;
}
else if (dp[i] == max)
{
++ans;
}
}
return ans;
}
};
如果nums中有n个元素,此算法时间复杂度为O(2 n ^n n),空间复杂度为O(2 n ^n n)。相比法一,是用空间换时间。