“Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。”
一、题目描述:
2044. 统计按位或能得到最大值的子集数目
给你一个整数数组 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] <= 10^5
二、思路分析:
位运算遍历
对于 nums.length为n的数组,总共有2^n个子集,对于空子集不符合题目要求,故有2^n -1个子集情况。有题目可知,n最大为16,可以一一遍历,判断是否为最大值。
我们用n为二进制数表示num[i]位是否被选中,即该对于的二进制数位是否为1.判断是否为最大值,为对应的数量cnt+1,若比最大值还大,更新最大值,清零cnt,使cnt=1
/**
* @param {number[]} nums
* @return {number}
*/
var countMaxOrSubsets = function(nums) {
let max=0,cnt=0,n=1<<nums.length
for(let i=0;i<n;i++){
let value=0;
for(let j=0;j<nums.length;j++){
if((1<<j)&i){
value|=nums[j]
}
}
if(value>max){
cnt=0;
cnt++;
max=value;
}else if(value===max){
cnt++;
}
}
return cnt
};
dfs
位运算遍历的方法,我们发现对于每一种情况都要重新计算对于的与运算集,重复运算。同时我们注意到子集的数量说到底,就是对每一个num【i】是否选中,即dfs(i+1,value|nums[i]) dfs(i+1,value)。我们可以使用dfs方法,一边遍历子集情况,且当i为n时计算是否为最大值。为对应的数量cnt+1,若比最大值还大,更新最大值,清零cnt,使cnt=1
/**
* @param {number[]} nums
* @return {number}
*/
var countMaxOrSubsets = function(nums) {
let max=0,cnt=0,n=nums.length
const dfs=function(index,value){
if(index===n){
if(value>max){
cnt=0;
max=value;
cnt++;
}else if(value===max){
cnt++;
}
return;
}
dfs(index+1,value|nums[index])
dfs(index+1,value)
}
dfs(0,0)
return cnt
};
优化一:减少子集判断情况
因为是求最大的与运算的结果,明显可知对于该数组全体与运算的值肯定为最大值。这样我们使用dfs运算时只需要判断当前值是否已经等于最大值了,若等于 后面的n-i位可以取也可以不取,总共有2^(n-i)中情况,否则若i===n结束遍历,或者继续dfs(i+1,value|nums[i]) dfs(i+1,value)
/**
* @param {number[]} nums
* @return {number}
*/
var countMaxOrSubsets = function(nums) {
let max=0,cnt=0,n=nums.length
for(let i of nums)max|=i;
const dfs=function(index,value){
if(value===max){
cnt+=1<<(n-index);
return;
}
dfs(index+1,value|nums[index])
dfs(index+1,value)
}
dfs(0,0)
return cnt
};
优化二:减少不选择的次数
我们发现,对于i-1位不选择i位和不选择i+1位,是相当于在i-1位遍历里面直接选择了i+2位,即
dfs(i+2,value|nums[i-1])
故我们可以遍历当前i位后的数进行dfs循环,减少不选择的重复次数
/**
* @param {number[]} nums
* @return {number}
*/
var countMaxOrSubsets = function(nums) {
let max=0,cnt=0,n=nums.length
for(let i of nums)max|=i;
const dfs=function(index,value){
if(value===max){
cnt+=1<<(n-index);
return;
}
for(let j=index;j<n;j++){
dfs(j+1,value|nums[j])
}
}
dfs(0,0)
return cnt
};