LeetCode——1787. 使所有区间的异或结果为零[Make the XOR of All Segments Equal to Zero][困难]——分析及代码[Java]
一、题目
给你一个整数数组 nums 和一个整数 k 。区间 [left, right](left <= right)的 异或结果 是对下标位于 left 和 right(包括 left 和 right )之间所有元素进行 XOR 运算的结果:nums[left] XOR nums[left+1] XOR … XOR nums[right] 。
返回数组中 要更改的最小元素数 ,以使所有长度为 k 的区间异或结果等于零。
示例 1:
输入:nums = [1,2,0,3,0], k = 1
输出:3
解释:将数组 [1,2,0,3,0] 修改为 [0,0,0,0,0]
示例 2:
输入:nums = [3,4,5,2,1,7,3,4,7], k = 3
输出:3
解释:将数组 [3,4,5,2,1,7,3,4,7] 修改为 [3,4,7,3,4,7,3,4,7]
示例 3:
输入:nums = [1,2,4,1,2,5,1,2,6], k = 3
输出:3
解释:将数组[1,2,4,1,2,5,1,2,6] 修改为 [1,2,3,1,2,3,1,2,3]
提示:
- 1 <= k <= nums.length <= 2000
- 0 <= nums[i] < 2^10
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/make-the-xor-of-all-segments-equal-to-zero
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、分析及代码
1. 分组 + 动态规划
(1)思路
根据题目特点,最后所有长度为 k 的区间异或结果等于零,可推出得到的数组满足:
- a1 = ak+1 = a2k+1 = …
- a2 = ak+2 = a2k+2 = …
因此可将数组中的元素按上述规律,每间隔 k 个的数字为一组进行分组。
在此基础上,设计一个动态规划数组 dp[j],表示到当前第 i 组为止,所有元素异或到对应数字时的更改次数。则对第 i 组 dp[j] 的状态转移方程可能为:
- j 可由某一数值和当前组中的某个数 num 异或得到,newDp[j] = dp[j & num] + size[i] - 组中 num 的数量
- j 可通过和任意数字异或得到,newDp[j] = 前一 dp 中最小的改变次数 + size[i]
完成 k 个组的动态规划后,dp[0]就是所求的解。
详细思路可参考:https://leetcode-cn.com/problems/make-the-xor-of-all-segments-equal-to-zero/solution/an-yu-shu-fen-zu-jin-xing-dong-tai-gui-h-7z0g/
(2)代码
class Solution {
public int minChanges(int[] nums, int k) {
int n = nums.length;
List<Map<Integer, Integer>> group = new ArrayList<Map<Integer, Integer>>();//存储各组中各个数字数量
int[] size = new int[k];//各组大小
Arrays.fill(size, 0);
for (int i = 0; i < k; i++) {
Map map = new HashMap<Integer, Integer>();
for (int j = i; j < n; j += k) {
map.put(nums[j], (int)map.getOrDefault(nums[j], 0) + 1);
size[i]++;
}
group.add(map);
}
int range = 1 << 10;//题中nums[i] < 2^10
int[] dp = new int[range];//当前组异或到对应数字时的更改次数
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for (int i = 0; i < k; i++) {
int minNum = Integer.MAX_VALUE;
for (int d : dp)
minNum = Math.min(minNum, d);
int[] newDp = new int[range];//新建一个数组,避免与上一组结果干扰
Arrays.fill(newDp, minNum + size[i]);//变为当前组中不存在数字的改变次数:之前的最小改变次数+当前组大小
for (int j = 0; j < range; j++) {
if (dp[j] == Integer.MAX_VALUE)//对第0组,保证dp[i]是异或到0的最小改变数
continue;
Iterator it = group.get(i).entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
int number = (int)entry.getKey(), s = (int)entry.getValue();
int xorNum = number ^ j;
newDp[xorNum] = Math.min(newDp[xorNum], dp[j] + size[i] - s);
}
}
for (int j= 0; j < range; j++)//将当前组的dp复制入总dp
dp[j] = newDp[j];
}
return dp[0];
}
}
(3)结果
执行用时 :635 ms,在所有 Java 提交中击败了 53.06% 的用户;
内存消耗 :39.8 MB,在所有 Java 提交中击败了 75.51% 的用户。
三、其他
暂无。