题目地址:
https://leetcode.com/problems/maximum-xor-with-an-element-from-array/
给定一个长 n n n的非负整数数组 A A A,再给定一系列询问 q q q,每次询问包含两个数字 x x x和 m m m,要求找到 A A A中小于等于 m m m并且与 x x x异或值最大的那个数。如果不存在则返回 − 1 -1 −1。题目保证询问的数字也都是非负整数。
一个比较简单的想法是用Trie,并且在每个节点里还存放一下走到当前路径的所有数的最小值。接下来询问的时候,对于每次询问 ( x , m ) (x,m) (x,m),把 x x x转为长 31 31 31的二进制数,然后从高位开始遍历,并且从Trie树根向下走,优先走与 x x x当前二进制位不同的那个分支,如果那个分支的最小值是大于 m m m的,那无解,走不了(否则就走),则考虑走另一个分支,如果那个分支的最小值也是大于 m m m的,则本次询问无解,返回 − 1 -1 −1(否则就走这个分支);否则就按照上面的原则,走下去。走的同时记录异或的答案即可。代码如下:
public class Solution {
class Node {
int min;
Node next[];
public Node() {
min = Integer.MAX_VALUE;
next = new Node[2];
}
}
// 将x的二进制表示插入该Trie中
private void insert(int x) {
Node cur = root;
for (int i = 31; i >= 0; i--) {
int idx = x >> i & 1;
if (cur.next[idx] == null) {
cur.next[idx] = new Node();
}
cur = cur.next[idx];
// 更新路径最小值
cur.min = Math.min(cur.min, x);
}
}
private int query(int x, int lim) {
Node cur = root;
int res = 0;
for (int i = 31; i >= 0; i--) {
int idx = x >> i & 1;
res <<= 1;
if (cur.next[idx ^ 1] != null && cur.next[idx ^ 1].min <= lim) {
cur = cur.next[idx ^ 1];
res++;
} else if (cur.next[idx] != null && cur.next[idx].min <= lim) {
cur = cur.next[idx];
} else {
return -1;
}
}
return res;
}
Node root;
public int[] maximizeXor(int[] nums, int[][] queries) {
root = new Node();
for (int num : nums) {
insert(num);
}
int[] res = new int[queries.length];
for (int i = 0; i < res.length; i++) {
int[] query = queries[i];
res[i] = query(query[0], query[1]);
}
return res;
}
}
预处理时间复杂度 O ( n ) O(n) O(n),每次询问时间 O ( 1 ) O(1) O(1),空间 O ( n ) O(n) O(n)。
还有个改进的做法,就是将 A A A从小到大排序,然后将询问按照 m m m也排序,然后适当安排插入和查询的次序,这样就不用在Trie上记录最小值了。代码如下:
import java.util.Arrays;
public class Solution {
class Node {
Node next[];
public Node() {
next = new Node[2];
}
}
private void insert(int x) {
Node cur = root;
for (int i = 31; i >= 0; i--) {
int idx = x >> i & 1;
if (cur.next[idx] == null) {
cur.next[idx] = new Node();
}
cur = cur.next[idx];
}
}
private int query(int x) {
Node cur = root;
int res = 0;
for (int i = 31; i >= 0; i--) {
int idx = x >> i & 1;
res <<= 1;
if (cur.next[idx ^ 1] != null) {
cur = cur.next[idx ^ 1];
res++;
} else if (cur.next[idx] != null) {
cur = cur.next[idx];
} else {
return -1;
}
}
return res;
}
Node root;
public int[] maximizeXor(int[] nums, int[][] queries) {
root = new Node();
Arrays.sort(nums);
for (int i = 0; i < queries.length; i++) {
queries[i] = new int[]{
queries[i][0], queries[i][1], i};
}
Arrays.sort(queries, (q1, q2) -> Integer.compare(q1[1], q2[1]));
int[] res = new int[queries.length];
for (int i = 0, j = 0; j < queries.length; j++) {
// 把小于等于当前limit的数insert进Trie
while (i < nums.length && nums[i] <= queries[j][1]) {
insert(nums[i]);
i++;
}
// 这样答案如果存在,就一定已经在Trie里了,直接query即可
res[queries[j][2]] = query(queries[j][0]);
}
return res;
}
}
总时间复杂度 O ( n log n + l q log l q ) O(n\log n+l_q\log l_q) O(nlogn+lqloglq),每次询问时间 O ( 1 ) O(1) O(1)。