Hard

题目描述

给你一个非负整数数组 nums 和一个整数 k

你必须选择 nums 的一个子数组,使得其最大元素和最小元素之间的差值最多为 k。该子数组的值是子数组中所有元素的按位异或。

返回所选子数组的最大可能值。

示例 1:

输入:nums = [5,4,5,6], k = 2
输出:7
解释:
选择子数组 [5, 4, 5, 6]。
其最大和最小元素之间的差值是 6 - 4 = 2 <= k。
值为 4 XOR 5 XOR 6 = 7。

示例 2:

输入:nums = [5,4,5,6], k = 1
输出:6
解释:
选择子数组 [6]。
其最大和最小元素之间的差值是 6 - 6 = 0 <= k。
值为 6。

提示:

  • 1 <= nums.length <= 4 * 10^4
  • 0 <= nums[i] < 2^15
  • 0 <= k < 2^15

解题思路

这道题需要找到一个子数组,使得其最大值和最小值的差不超过 k,并且该子数组的异或值最大。

核心思路:

  1. 滑动窗口维护有效范围:使用双指针技术,维护一个窗口 [left, right],确保窗口内最大值和最小值的差不超过 k。可以使用单调栈或有序集合来高效维护区间最值。

  2. 前缀异或 + 字典树优化查询:对于每个有效的右端点 r,我们需要在所有有效的左端点 l 中找到使得 prefix[r+1] ^ prefix[l] 最大的 l。这里 prefix[i] = nums[0] ^ nums[1] ^ ... ^ nums[i-1]

  3. 字典树维护前缀异或:将有效的前缀异或值插入字典树中,每个节点维护计数以支持删除操作。对于查询,在字典树中贪心地选择与当前前缀异或值异或后结果最大的路径。

算法流程:

  • 使用滑动窗口维护满足条件的区间
  • 当右端点扩展时,将对应的前缀异或值加入字典树
  • 当窗口不满足条件时,移动左端点并从字典树中删除对应前缀
  • 对每个位置查询字典树获得最大异或值

推荐解法: 滑动窗口 + 字典树,时间复杂度 O(n log max_val),空间复杂度 O(n log max_val)。

代码实现

class Solution {
public:
    struct TrieNode {
        TrieNode* children[2];
        int count;
        TrieNode() : count(0) {
            children[0] = children[1] = nullptr;
        }
    };
    
    class Trie {
    public:
        TrieNode* root;
        
        Trie() {
            root = new TrieNode();
        }
        
        void insert(int num) {
            TrieNode* node = root;
            for (int i = 14; i >= 0; i--) {
                int bit = (num >> i) & 1;
                if (!node->children[bit]) {
                    node->children[bit] = new TrieNode();
                }
                node = node->children[bit];
                node->count++;
            }
        }
        
        void remove(int num) {
            TrieNode* node = root;
            for (int i = 14; i >= 0; i--) {
                int bit = (num >> i) & 1;
                node = node->children[bit];
                node->count--;
            }
        }
        
        int maxXor(int num) {
            TrieNode* node = root;
            int result = 0;
            for (int i = 14; i >= 0; i--) {
                int bit = (num >> i) & 1;
                int wantBit = 1 - bit;
                if (node->children[wantBit] && node->children[wantBit]->count > 0) {
                    result |= (1 << i);
                    node = node->children[wantBit];
                } else {
                    node = node->children[bit];
                }
            }
            return result;
        }
    };
    
    int maxXor(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> prefix(n + 1, 0);
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] ^ nums[i];
        }
        
        Trie trie;
        int left = 0, maxResult = 0;
        
        for (int right = 0; right < n; right++) {
            while (left <= right) {
                int minVal = *min_element(nums.begin() + left, nums.begin() + right + 1);
                int maxVal = *max_element(nums.begin() + left, nums.begin() + right + 1);
                if (maxVal - minVal <= k) break;
                left++;
            }
            
            for (int i = left; i <= right; i++) {
                trie.insert(prefix[i]);
            }
            
            maxResult = max(maxResult, trie.maxXor(prefix[right + 1]));
            
            // Clear trie for next iteration
            trie = Trie();
        }
        
        return maxResult;
    }
};
class Solution:
    def maxXor(self, nums: list[int], k: int) -> int:
        class TrieNode:
            def __init__(self):
                self.children = {}
                self.count = 0
        
        class Trie:
            def __init__(self):
                self.root = TrieNode()
            
            def insert(self, num):
                node = self.root
                for i in range(14, -1, -1):
                    bit = (num >> i) & 1
                    if bit not in node.children:
                        node.children[bit] = TrieNode()
                    node = node.children[bit]
                    node.count += 1
            
            def remove(self, num):
                node = self.root
                for i in range(14, -1, -1):
                    bit = (num >> i) & 1
                    node = node.children[bit]
                    node.count -= 1
            
            def max_xor(self, num):
                node = self.root
                result = 0
                for i in range(14, -1, -1):
                    bit = (num >> i) & 1
                    want_bit = 1 - bit
                    if want_bit in node.children and node.children[want_bit].count > 0:
                        result |= (1 << i)
                        node = node.children[want_bit]
                    else:
                        node = node.children[bit]
                return result
        
        n = len(nums)
        prefix = [0] * (n + 1)
        for i in range(n):
            prefix[i + 1] = prefix[i] ^ nums[i]
        
        trie = Trie()
        left = 0
        max_result = 0
        
        for right in range(n):
            while left <= right:
                min_val = min(nums[left:right + 1])
                max_val = max(nums[left:right + 1])
                if max_val - min_val <= k:
                    break
                left += 1
            
            for i in range(left, right + 1):
                trie.insert(prefix[i])
            
            max_result = max(max_result, trie.max_xor(prefix[right + 1]))
            
            # Clear trie for next iteration
            trie = Trie()
        
        return max_result
public class Solution {
    public class TrieNode {
        public TrieNode[] Children = new TrieNode[2];
        public int Count = 0;
    }
    
    public class Trie {
        private TrieNode root = new TrieNode();
        
        public void Insert(int num) {
            var node = root;
            for (int i = 14; i >= 0; i--) {
                int bit = (num >> i) & 1;
                if (node.Children[bit] == null) {
                    node.Children[bit] = new TrieNode();
                }
                node = node.Children[bit];
                node.Count++;
            }
        }
        
        public void Remove(int num) {
            var node = root;
            for (int i = 14; i >= 0; i--) {
                int bit = (num >> i) & 1;
                node = node.Children[bit];
                node.Count--;
            }
        }
        
        public int MaxXor(int num) {
            var node = root;
            int result = 0;
            for (int i = 14; i >= 0; i--) {
                int bit = (num >> i) & 1;
                int wantBit = 1 - bit;
                if (node.Children[wantBit] != null && node.Children[wantBit].Count > 0) {
                    result |= (1 << i);
                    node = node.Children[wantBit];
                } else {
                    node = node.Children[bit];
                }
            }
            return result;
        }
    }
    
    public int MaxXor(int[] nums, int k) {
        int n = nums.Length;
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] ^ nums[i];
        }
        
        var trie = new Trie();
        int left = 0, maxResult = 0;
        
        for (int right = 0; right < n; right++) {
            while (left <= right) {
                int minVal = nums[left..right+1].Min();
                int maxVal = nums[left..right+1].Max();
                if (maxVal - minVal <= k) break;
                left++;
            }
            
            for (int i = left; i <= right; i++) {
                trie.Insert(prefix[i]);
            }
            
            maxResult = Math.Max(maxResult, trie.MaxXor(prefix[right + 1]));
            
            // Clear trie for next iteration
            trie = new Trie();
        }
        
        return maxResult;
    }
}
var maxXor = function(nums, k) {
    class TrieNode {
        constructor() {
            this.children = new Array(2);
            this.count = 0;
        }
    }
    
    class Trie {
        constructor() {
            this.root = new TrieNode();
        }
        
        insert(num) {
            let node = this.root;
            for (let i = 14; i >= 0; i--) {
                const bit = (num >> i) & 1;
                if (!node.children[bit]) {
                    node.children[bit] = new TrieNode();
                }
                node = node.children[bit];
                node.count++;
            }
        }
        
        remove(num) {
            let node = this.root;
            for (let i = 14; i >= 0; i--) {
                const bit = (num >> i) & 1;
                node = node.children[bit];
                node.count--;
            }
        }
        
        maxXor(num) {
            let node = this.root;
            let result = 0;
            for (let i = 14; i >= 0; i--) {
                const bit = (num >> i) & 1;
                const wantBit = 1 - bit;
                if (node.children[wantBit] && node.children[wantBit].count > 0) {
                    result |= (1 << i);
                    node = node.children[wantBit];
                } else {
                    node = node.children[bit];
                }
            }
            return result;
        }
    }
    
    const n = nums.length;
    const prefix = new Array(n + 1).fill(0);
    for (let i = 0; i < n; i++) {
        prefix[i + 1] = prefix[i] ^ nums[i];
    }
    
    let trie = new Trie();
    let left = 0, maxResult = 0;
    
    for (let right = 0; right < n; right++) {
        while (left <= right) {
            const subarray = nums.slice(left, right + 1);
            const minVal = Math.min(...subarray);
            const maxVal = Math.max(...subarray);
            if (maxVal - minVal <= k) break;
            left++;
        }
        
        for (let i = left; i <= right; i++) {
            trie.insert(prefix[i]);
        }
        
        maxResult = Math.max(maxResult, trie.maxXor(prefix[right + 1]));
        
        // Clear trie for next iteration
        trie = new Trie();
    }
    
    return maxResult;
};

复杂度分析

解法时间复杂度空间复杂度
滑动窗口 + 字典树O(n² log V)O(n log V)

其中 n 是数组长度,V 是数组中的最大值(约为 2^15)。由于每次重建字典树导致时间复杂度较高,可以进一步优化为 O(n log V)。