LeetCode 2588. 统计美丽子数组数目

2588. 统计美丽子数组数目

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

  • 选择两个满足 0 <= i, j < nums.length 的不同下标 i 和 j 。
  • 选择一个非负整数 k ,满足 nums[i] 和 nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1 。
  • 将 nums[i] 和 nums[j] 都减去 2^k 。

如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 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 个数字都减去 2^1 ,子数组变成 [1, 1, 0] 。
  - 选择 [1, 1, 0] 和 k = 0 。将 2 个数字都减去 2^0 ,子数组变成 [0, 0, 0] 。
- 按照下述步骤,我们可以将子数组 [4,3,1,2,4] 中所有元素变成 0 :
  - 选择 [4, 3, 1, 2, 4] 和 k = 2 。将 2 个数字都减去 2^2,子数组变成 [0, 3, 1, 2, 0] 。
  - 选择 [0, 3, 1, 2, 0] 和 k = 0 。将 2 个数字都减去 2^0,子数组变成 [0, 2, 0, 2, 0] 。
  - 选择 [0, 2, 0, 2, 0] 和 k = 1 。将 2 个数字都减去 2^1,子数组变成 [0, 0, 0, 0, 0] 。

示例 2:

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

提示:

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^6

提示 1

A subarray is beautiful if its xor is equal to zero.


提示 2

Compute the prefix xor for every index, then the xor of subarray [left, right] is equal to zero if prefix_xor[left] ^ perfix_xor[right] == 0


提示 3

Iterate from left to right and maintain a hash table to count the number of indices equal to the current prefix xor.

解法:前缀异或和

提示 1

由于每次操作的都是同一个比特位,可以把每一位单独看。

提示 2

每次都去掉两个 1,要是美丽子数组,需要子数组内这个比特位的 1 的个数是偶数。

提示 3

由于 1⊕1=0,把所有比特位合起来看,美丽子数组这等价于子数组的异或和等于 0。

提示 4

利用前缀异或和 s,问题相当于在求 s 中有多少对 s[left] 和 s[right] 满足 s[right]⊕s[left]=0,即 s[right]=s[left],因为异或为 0 的两个数字必然相等。

也就是说,我们实际计算的是 s 中有多少对相同数字。

我们可以一边遍历 prefix,一边用一个哈希表 count 统计 s[i] 的出现次数,累加遍历中的 count[s[i]],即为答案。

Java版:

class Solution {
    public long beautifulSubarrays(int[] nums) {
        int n = nums.length;
        long ans = 0;
        int[] prefix = new int[n + 1];
        for (int i = 1; i < n + 1; i++) {
            prefix[i] = prefix[i - 1] ^ nums[i - 1];
        } 
        Map<Integer, Integer> count = new HashMap<>();
        for (int p: prefix) {
            ans += count.getOrDefault(p, 0);
            count.merge(p, 1, Integer::sum);
        }
        return ans;
    }
}

Python3版:

class Solution:
    def beautifulSubarrays(self, nums: List[int]) -> int:
        n = len(nums)
        ans = 0
        prefix = [0] * (n + 1)
        for i in range(1, n + 1):
            prefix[i] = prefix[i - 1] ^ nums[i - 1]
        
        count = dict()
        for p in prefix:
            if p in count:
                ans += count[p]
                count[p] += 1
            else:
                count[p] = 1
        return ans

复杂度分析

  • 时间复杂度:O(n),其中 n 为 nums 的长度。
  • 空间复杂度:O(n)。

另一种写法:

一边计算prefix,一边统计答案。

Java版:

class Solution {
    public long beautifulSubarrays(int[] nums) {
        int n = nums.length;
        long ans = 0;
        int prefix = 0;
        Map<Integer, Integer> count = new HashMap<>();
        count.put(0, 1);
        for (int i = 0; i < nums.length; i++) {
            prefix ^= nums[i];
            ans += count.getOrDefault(prefix, 0);
            count.merge(prefix, 1, Integer::sum);
        }
        return ans;
    }
}

Python3版:

class Solution:
    def beautifulSubarrays(self, nums: List[int]) -> int:
        ans = 0
        count = dict()
        count[0] = 1
        prefix = 0
        for num in nums:
            prefix ^= num
            if prefix in count:
                ans += count[prefix]
                count[prefix] += 1
            else:
                count[prefix] = 1
        return ans

复杂度分析

  • 时间复杂度:O(n),其中 n 为 nums 的长度。
  • 空间复杂度:O(n)。
  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值