携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
题目链接:2317. 操作后的最大异或和
题目描述
给你一个下标从 0 开始的整数数组 nums 。一次操作中,选择 任意 非负整数 x 和一个下标 i ,更新 nums[i] 为 nums[i] AND (nums[i] XOR x) 。
注意,AND 是逐位与运算,XOR 是逐位异或运算。
请你执行 任意次 更新操作,并返回 nums 中所有元素 最大 逐位异或和。
提示:
示例 1:
输入:nums = [3,2,4,6]
输出:7
解释:选择 x = 4 和 i = 3 进行操作,num[3] = 6 AND (6 XOR 4) = 6 AND 2 = 2 。
现在,nums = [3, 2, 4, 2] 且所有元素逐位异或得到 3 XOR 2 XOR 4 XOR 2 = 7 。
可知 7 是能得到的最大逐位异或和。
注意,其他操作可能也能得到逐位异或和 7 。
示例 2:
输入:nums = [1,2,3,9,2]
输出:11
解释:执行 0 次操作。
所有元素的逐位异或和为 1 XOR 2 XOR 3 XOR 9 XOR 2 = 11 。
可知 11 是能得到的最大逐位异或和。
整理题意
题目给定数组 nums,为了使得数组最后所有整数异或的值最大,我们可以对数组中的元素进行 任意次 更新操作,将 nums[i] 更新为 nums[i] AND (nums[i] XOR x)。
最后返回更新后的数组中所有整数异或的最大值。
解题思路分析
首先要明白两个运算规则:
- 异或:相同为零,相异为一。
- 与:只有都为一的时候才为一,其他情况为零。
再来观察这个更新操作:nums[i] = nums[i] AND (nums[i] XOR x)
- 先不看小括号里的运算,最后运算为
nums[i]和括号运算结果进行与操作,那么可能得到的最大值也就为nums[i],也就是最多只能保留nums[i]二进制下已有的1。 - 再来看括号里的运算,我们可以取任意
x来改变nums[i]为任意数,换句话说也就是括号里的数可以由我们决定,改变为我们想要的任意数。 综上,我们可以得到结论:我们的更新操作可以将nums[i]二进制下某些位上的1改变为0,但无法将某些位上的0改变为1。
由异或运算规则相异为一可知,对于二进制下某一位来说,如果数组所有整数在该位上 1 的个数为奇数个,那么最后该位异或值就为 1,那么为了构造出使得最后异或值最大,我们希望数组中所有数在二进制下每一位的和都为奇数个。如果二进制下某一位的和为偶数个 1 时(非 0 偶数),我们可以采取更新操作使得该位 1 的个数减少一个变为奇数个。
具体实现
- 按照解题思路模拟,我们依次统计数组中二进制下每一位上
1的个数。 - 最后将二进制下每一位
1的个数统计不为0的值进行累加答案。
优化
根据具体实现中的第 2 步我们可以得知,对于二进制下每一位来说,只要数组中有一个整数在该位上有 1 即可,那么我们直接将数组中的所有整数 按位或 即可得到答案。
按位或运算规则:只有全为零时才为零,其他情况为一。
复杂度分析
- 时间复杂度:,
n为数组长度,仅需遍历一遍数组。 - 空间复杂度:,仅需常数空间。
代码实现
class Solution {
public:
int maximumXOR(vector<int>& nums) {
int ans = 0;
for(int x : nums) ans |= x;
return ans;
}
};
总结
- 该题为脑筋急转弯题目,可以用 贪心 的思想进行解题。
- 因为更新操作最后需要与
nums[i]进行 与 操作,所以只能将nums[i]二进制下部分1置为0,而不能将0置为1,因此问题转化为求将nums[i]中二进制位任意1转化为0后的最大异或值。 - 那么可得最优解为二进制下相同位只用保留
1个1即可,将其余相同位置为0,有1即可,可以想到或运算,那么最终最优解就为数组中所有整数按位或即为答案。 - 测试结果:
结束语
我们偶尔会发现自己的脚步停滞不前,与目标只差一点点距离,其实,那一点距离的名字叫坚持。无论今天要面对什么,既然走到这一步,就坚持下去,给自己一些肯定,相信多努力一点,我们就能抵达想去的远方。新的一天,加油!