LeetCode 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:
python
输入: nums = [4,3,1,2,4]
输出: 2
示例 2:
python
输入: nums = [1,10,4]
输出: 0
解题思路
本题的核心在于:如果一个子数组是美丽的,那么它的所有元素异或后的结果必然为 0。
我们可以利用 前缀异或和 + 哈希表 来高效计算满足条件的子数组数量。
前缀异或的思路
- 设
preXor[i]
表示从数组起点到索引i
的前缀异或和。 - 如果某一段子数组
nums[l..r]
异或和为0
,那么它满足美丽子数组的条件。 - 由于
preXor[i] = preXor[j]
表示nums[j+1..i]
这段子数组的异或和为0
,因此我们可以使用哈希表cnt
统计preXor
出现的次数。 - 每次计算当前
preXor[i]
时,累加cnt[preXor[i]]
到答案中。
代码实现
python
from collections import defaultdict
class Solution:
def beautifulSubarrays(self, nums: List[int]) -> int:
dict1 = defaultdict(int) # 存储前缀异或和出现次数
ans, cur = 0, 0
dict1[0] = 1 # 处理从索引 0 开始就是美丽子数组的情况
for num in nums:
cur ^= num # 计算当前的前缀异或和
ans += dict1[cur] # 统计当前前缀异或和已出现的次数
dict1[cur] += 1 # 记录当前异或和出现的次数
return ans
代码解析
cur
:记录当前前缀异或和。dict1[cur]
:记录前缀异或和cur
出现的次数。dict1[0] = 1
:表示当cur == 0
时,说明从0
到i
的子数组本身是美丽子数组。- 遍历
nums
,每次更新cur ^= num
计算当前前缀异或和。 ans += dict1[cur]
统计cur
之前出现的次数,这些都是满足异或和为0
的子数组。- 最后更新
dict1[cur]
以记录当前cur
的次数。
时间复杂度分析
- 时间复杂度 :
O(n)
,遍历nums
一次,每次操作均为O(1)
。 - 空间复杂度 :
O(n)
,哈希表dict1
最坏情况下存储n
个不同的前缀异或和。
示例分析
示例 1
python
nums = [4,3,1,2,4]
计算前缀异或和并统计 dict1
:
i | nums[i] | cur (前缀异或) | dict1 计数 | ans (累计美丽子数组) |
---|---|---|---|---|
0 | 4 | 4 | {0:1, 4:1} | 0 |
1 | 3 | 7 | {0:1, 4:1, 7:1} | 0 |
2 | 1 | 6 | {0:1, 4:1, 7:1, 6:1} | 0 |
3 | 2 | 4 | {0:1, 4:2, 7:1, 6:1} | 1 |
4 | 4 | 0 | {0:2, 4:2, 7:1, 6:1} | 2 |
最终答案 ans = 2
,表示 nums
中存在两个美丽子数组。
示例 2
python
nums = [1,10,4]
- 计算前缀异或和
cur
依次为[1, 11, 15]
,它们都没有重复出现,因此ans = 0
。
总结
- 核心思想 :利用前缀异或和的性质,判断某个子数组是否异或和为
0
。 - 哈希表优化 :利用
dict1
记录cur
出现的次数,实现O(n)
复杂度计算。 - 适用范围 :适用于寻找连续子数组异或和特定值 的问题,比如
560. 和为 K 的子数组
。
这道题的解法很巧妙,是 前缀异或 + 哈希表 的经典应用,值得深入理解!