“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”
一、题目描述
给你一个整数数组 nums,请你找出 nums 子集按位或可能得到的最大值,并返回按位或能得到最大值的不同非空子集的数目。如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为数组 a 是数组 b 的一个子集。如果选择的元素下标位置不一样,则认为两个子集不同。对数组 a 执行按位或,结果等于 a[0] OR a[1] OR ... OR a[a,length-1](下标从0开始)。
输入:nums = [3,1]
输出:2
解释:子集按位或能得到的最大值是 3 。有 2 个子集按位或可以得到 3 :
- [3]
- [3,1]
输入:nums = [2,2,2]
输出:7
解释:[2,2,2] 的所有非空子集的按位或都可以得到 2 。总共有 23 - 1 = 7 个子集。
二、思路分析
本题可以使用位运算进行操作。我们可以将 nums 数组中的每个元素对应到一个由二进制表示的整数的每一位,其中二进制数的长度 n=nums.length,之后从 i = 0 开始循环到 i=n-1,当 i 的二进制位为 1 时,对应的选取 nums 中的元素;为 0 则不选取。例如,i=3,nums=[3,1],当 i=3 时二进制表示为 i=1 1,所以选取 nums 数组中的 3,1 两项;若 i=2 则仅选取 3。这样我们就可以暴力穷举 nums 所有可能的子数组,并将各个子数组按位或,得到结果后进行比较大小,统计。
注意:在判断是否要选取数组中的某个元素时,可以先将 i 右移 k(数组中元素的索引)位,之后与1做 & 操作,若结果为1则选取该元素,否则不选取该元素。例如,i=3,nums=[3,1],要判断是否选取元素 1,先将 i 右移 1 位,再与1做 & 操作。
三、代码
func countMaxOrSubsets(nums []int) int {
max := 0
res := 0
for i := 0; i < 1<<len(nums); i++ {
temp := 0
for k, v := range nums {
if i>>k&1 == 1 {
temp |= v
}
}
if temp > max {
max = temp
res = 1
} else if temp == max {
res++
}
}
return res
}
四、总结
学习了使用位运算来进行状态压缩,节省了存储空间。并且学习了如何得到使用二进制表示数的每一位。