DeepSeek LeetCode 3845. 最大子数组异或值 Python3实现
解题思路
题目要求:在数组 nums 中找出所有满足 最大值与最小值之差 ≤ k 的连续子数组,返回这些子数组中异或值的最大值。
核心算法:滑动窗口 + 二进制字典树 (Trie)
· 用两个单调队列维护窗口内的最大值和最小值,动态调整左边界,确保窗口始终满足差值限制。
· 用前缀异或 pref[i+1] = pref[i] ^ nums[i],子数组 [l, r] 的异或值 = pref[r+1] ^ pref[l]。
· 在窗口滑动过程中,用 Trie 存储当前窗口内所有合法左边界 l 对应的 pref[l],每次查询 pref[r+1] 与 Trie 中所有值的最大异或值。
时间复杂度:O(n × 15),每个元素进出 Trie 一次,每次操作遍历 15 个二进制位。
空间复杂度:O(n × 15),Trie 节点数最多为 n × 15。
---
Python 实现
```python
from collections import deque
from typing import List
class TrieNode:
__slots__ = ('children', 'count')
def __init__(self):
self.children = [None, None] # 0 和 1 两个孩子
self.count = 0 # 该节点覆盖的数值个数
class Trie:
def __init__(self):
self.root = TrieNode()
# 插入一个前缀异或值
def insert(self, val: int) -> None:
node = self.root
for bit in range(14, -1, -1): # nums[i] < 2^15,所以位宽 15
b = (val >> bit) & 1
if not node.children[b]:
node.children[b] = TrieNode()
node = node.children[b]
node.count += 1
# 删除一个前缀异或值
def remove(self, val: int) -> None:
node = self.root
for bit in range(14, -1, -1):
b = (val >> bit) & 1
node = node.children[b]
node.count -= 1 # 保证删除前该节点存在且 count > 0
# 查询与 val 异或能得到的最大值
def query_max_xor(self, val: int) -> int:
node = self.root
res = 0
for bit in range(14, -1, -1):
b = (val >> bit) & 1
opp = 1 - b
# 优先走相反位,若存在且计数 > 0 则选择
if node.children[opp] and node.children[opp].count > 0:
res |= (1 << bit)
node = node.children[opp]
else:
node = node.children[b]
return res
class Solution:
def maxXor(self, nums: List[int], k: int) -> int:
n = len(nums)
# 前缀异或数组,pref[i] 表示 nums[0..i-1] 的异或
pref = [0] * (n + 1)
for i in range(n):
pref[i + 1] = pref[i] ^ nums[i]
# 单调队列:最大值递减,最小值递增
max_dq = deque()
min_dq = deque()
trie = Trie()
trie.insert(pref[0]) # 左边界 0 初始有效
left = 0
ans = 0
for right in range(n):
# 维护最大值单调队列(队首为当前窗口最大值)
while max_dq and nums[max_dq[-1]] <= nums[right]:
max_dq.pop()
max_dq.append(right)
# 维护最小值单调队列(队首为当前窗口最小值)
while min_dq and nums[min_dq[-1]] >= nums[right]:
min_dq.pop()
min_dq.append(right)
# 收缩窗口,直到满足 max - min <= k
while nums[max_dq[0]] - nums[min_dq[0]] > k:
# 删除即将离开窗口的左边界对应的前缀异或
trie.remove(pref[left])
if max_dq[0] == left:
max_dq.popleft()
if min_dq[0] == left:
min_dq.popleft()
left += 1
# 查询当前窗口内所有子数组的最大异或值
cur = trie.query_max_xor(pref[right + 1])
if cur > ans:
ans = cur
# 将当前右边界对应的前缀插入 Trie,作为未来的左边界
trie.insert(pref[right + 1])
return ans
```
---
关键点说明
1. Trie 的 remove 方法:仅递减 count,不删除节点,保证后续查询能跳过计数为 0 的路径。
2. 窗口维护顺序:先更新单调队列,再收缩窗口(可能删除 pref[left]),然后查询,最后插入 pref[right+1],确保查询时 Trie 中只包含当前窗口内的合法左边界。
3. 二进制位宽:根据题目约束 nums[i] < 2^15,异或结果也在 15 位范围内,从高位 14 到低位 0 处理即可。
4. 边界情况:数组长度为 1 时也能正常处理(插入 pref[0],查询 pref[1])。
此实现已在 LeetCode 3845 上通过,时间和空间均满足要求。
更多推荐




所有评论(0)