题目描述
给你一个下标从 0 开始的整数数组 nums
和一个正整数 k
。
你可以对数组执行以下操作 任意次:
- 选择数组里的 任意 一个元素,并将它的 二进制 表示 翻转 一个数位,翻转数位表示将
0
变成1
或者将1
变成0
。
你的目标是让数组里 所有 元素的按位异或和得到 k
,请你返回达成这一目标的 最少 操作次数。
注意,你也可以将一个数的前导 0
翻转。比方说,数字 (101)_2
翻转第四个数位,得到 (1101)_2
。
样例
输入:nums = [2,1,3,4], k = 1
输出:2
解释:我们可以执行以下操作:
- 选择下标为 2 的元素,也就是 3 == (011)_2,我们翻转第一个数位得到 (010)_2 == 2。
数组变为 [2,1,2,4]。
- 选择下标为 0 的元素,也就是 2 == (010)_2,我们翻转第三个数位得到 (110)_2 == 6。
数组变为 [6,1,2,4]。
最终数组的所有元素异或和为 (6 XOR 1 XOR 2 XOR 4) == 1 == k。
无法用少于 2 次操作得到异或和等于 k。
输入:nums = [2,0,2,0], k = 0
输出:0
解释:数组所有元素的异或和为 (2 XOR 0 XOR 2 XOR 0) == 0 == k。所以不需要进行任何操作。
限制
1 <= nums.length <= 10^5
0 <= nums[i] <= 10^6
0 <= k <= 10^6
算法
(位运算) $O(n + \log m)$
- 可以先将所有数组进行异或,然后求出异或值与 $k$ 中二进制数位不同的数量。
- 进一步看,可以直接让 $k$ 去异或所有数字,然后求出异或结果二进制中 $1$ 的数量。
时间复杂度
- 遍历数组一次,然后遍历最大值为 $m$ 的二进制数位,时间复杂度为 $O(n + \log m)$。
空间复杂度
- 仅需要常数的额外空间。
C++ 代码
class Solution {
public:
int minOperations(vector<int>& nums, int k) {
for (int x : nums)
k ^= x;
int ans = 0;
for (; k; k -= k & -k)
++ans;
return ans;
}
};