【前缀异或和】力扣2588. 统计美丽子数组数目

给你一个下标从 0 开始的整数数组nums 。每次操作中,你可以:

选择两个满足 0 <= i, j < nums.length 的不同下标 i 和 j 。
选择一个非负整数 k ,满足 nums[i] 和 nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1 。
将 nums[i] 和 nums[j] 都减去 2k 。
如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0 的数组,那么我们称它是一个 美丽 的子数组。

请你返回数组 nums 中 美丽子数组 的数目。

子数组是一个数组中一段连续 非空 的元素序列。

示例 1:
输入:nums = [4,3,1,2,4]
输出:2
解释:nums 中有 2 个美丽子数组:[4,3,1,2,4] 和 [4,3,1,2,4] 。

  • 按照下述步骤,我们可以将子数组 [3,1,2] 中所有元素变成 0 :
    • 选择 [3, 1, 2] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [1, 1, 0] 。
    • 选择 [1, 1, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 0, 0] 。
  • 按照下述步骤,我们可以将子数组 [4,3,1,2,4] 中所有元素变成 0 :
    • 选择 [4, 3, 1, 2, 4] 和 k = 2 。将 2 个数字都减去 22 ,子数组变成 [0, 3, 1, 2, 0] 。
    • 选择 [0, 3, 1, 2, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 2, 0, 2, 0] 。
    • 选择 [0, 2, 0, 2, 0] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [0, 0, 0, 0, 0] 。

示例 2:
输入:nums = [1,10,4]
输出:0
解释:nums 中没有任何美丽子数组。

在这里插入图片描述

class Solution {
public:
    long long beautifulSubarrays(vector<int>& nums) {
        long long ans = 0;
        int s = 0;
        unordered_map<int,int> group = {{0,1}};
        for(int x : nums){
            s ^= x;
            ans += group[s];
            group[s]++;
        }
        return ans;

    }
};

这道题考虑到有2的幂次方,应该想到使用位运算的方法来做。由于是对子段的每两个数减去2^k,所以说明将该子段的元素都转换为二进制以后,每次减去2 ^k说明在相同位置的比特位上都减去1,也就是说,经过数次这样的操作后,最后所有元素都为0后,就是美丽的子数组。这就要求这个子段里的二进制中,一共要有偶数个1,进而转换为这个子段的异或和为0,说明是美丽的子数组。

第二个点是前缀异或和这个知识,比如 (1 ^ 2 ^ 3 ^ 4) ^ (1 ^ 2) = 3^4, 这就说明要求 3 和 4这个子段的异或和,就可以用较长的异或和去 异或 一个较短的异或和。

初始化unordered_map<int,int> group = {{0,1}};,然后遍历数组nums,用s来记录前缀异或和,group来记录前缀异或和出现过的次数,ans用来记录美丽前缀异或的数量。等于说不断枚举s,来看s之前有多少个前缀异或和 和s一样的数目(也就是美丽子数组的数目)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值