LeetCode——1787. 使所有区间的异或结果为零(Make the XOR of All Segments Equal to Zero)[困难]——分析及代码(Java)

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 组为止,所有元素异或到对应数字 j 时的更改次数。则对第 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% 的用户。

三、其他

暂无。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值