Problem#
You are given an array nums
consisting of non-negative integers. You are also given a queries
array, where queries[i] = [xi, mi]
.
The answer to the ith
query is the maximum bitwise XOR
value of xi
and any element of nums
that does not exceed mi
. In other words, the answer is
max(nums[j] XOR xi)
for all j
such that nums[j] <= mi
. If all elements in nums
are larger than mi
, then the answer is -1
.
Return an integer array answer
where answer.length == queries.length
and answer[i]
is the answer to the ith
query.
Examples#
Example 1#
1
2
3
4
5
6
7
8
9
10
Input: nums = [ 0 , 1 , 2 , 3 , 4 ], queries = [[ 3 , 1 ],[ 1 , 3 ],[ 5 , 6 ]]
Output: [ 3 , 3 , 7 ]
Explanation:
1 ) 0 and 1 are the only two integers not greater than 1. 0 XOR 3 = 3 and 1 XOR 3 = 2. The larger of the two is 3.
2 ) 1 XOR 2 = 3.
3 ) 5 XOR 2 = 7.
Example 2#
1
2
3
4
5
6
Input: nums = [ 5 , 2 , 4 , 6 , 6 , 3 ], queries = [[ 12 , 4 ],[ 8 , 1 ],[ 6 , 3 ]]
Output: [ 15 ,- 1 , 5 ]
Constraints#
1 <= nums.length, queries.length <= 10^5
queries[i].length == 2
0 <= nums[j], xi, mi <= 10^9
Solution#
Method 1 – Offline Queries with Trie#
Intuition#
To efficiently answer each query, we need to quickly find the maximum XOR of xi
with any nums[j]
not exceeding mi
. A Trie (prefix tree) can help us find the best XOR match for each query. By sorting both nums
and queries by mi
, we can insert eligible nums[j]
into the Trie as we process each query.
Approach#
Sort nums
in ascending order.
For each query, sort queries by mi
and keep track of their original indices.
Use a Trie to insert numbers from nums
that are <= mi
for the current query.
For each query, if Trie is not empty, find the maximum XOR of xi
with any number in Trie; else, answer is -1
.
Restore answers to the original query order.
Code#
Cpp
Go
Java
Kotlin
Python
Rust
Typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Trie {
Trie* child[2 ] = {};
public :
void insert(int num) {
Trie* node = this ;
for (int i = 30 ; i >= 0 ; -- i) {
int b = (num >> i) & 1 ;
if (! node-> child[b]) node-> child[b] = new Trie();
node = node-> child[b];
}
}
int getMaxXor (int num) {
Trie* node = this ;
int ans = 0 ;
for (int i = 30 ; i >= 0 ; -- i) {
int b = (num >> i) & 1 ;
if (node-> child[1 - b]) {
ans |= (1 << i);
node = node-> child[1 - b];
} else if (node-> child[b]) {
node = node-> child[b];
} else {
return - 1 ;
}
}
return ans;
}
};
class Solution {
public :
vector< int > maximizeXor(vector< int >& nums, vector< vector< int >>& queries) {
sort(nums.begin(), nums.end());
vector< pair< int , pair< int , int >>> q;
for (int i = 0 ; i < queries.size(); ++ i)
q.push_back({queries[i][1 ], {queries[i][0 ], i}});
sort(q.begin(), q.end());
vector< int > ans(queries.size(), - 1 );
Trie trie;
int idx = 0 ;
for (auto & [mi, p] : q) {
int xi = p.first, i = p.second;
while (idx < nums.size() && nums[idx] <= mi) trie.insert(nums[idx++ ]);
if (idx) ans[i] = trie.getMaxXor(xi);
}
return ans;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
type Trie struct {
child [2 ]* Trie
}
func (t * Trie ) insert (num int ) {
node := t
for i := 30 ; i >= 0 ; i -- {
b := (num >> i ) & 1
if node .child [b ] == nil {
node .child [b ] = & Trie {}
}
node = node .child [b ]
}
}
func (t * Trie ) getMaxXor (num int ) int {
node := t
ans := 0
for i := 30 ; i >= 0 ; i -- {
b := (num >> i ) & 1
if node .child [1 - b ] != nil {
ans |= 1 << i
node = node .child [1 - b ]
} else if node .child [b ] != nil {
node = node .child [b ]
} else {
return - 1
}
}
return ans
}
func maximizeXor (nums []int , queries [][]int ) []int {
sort .Ints (nums )
type Q struct { mi , xi , idx int }
q := make([]Q , len(queries ))
for i , v := range queries {
q [i ] = Q {v [1 ], v [0 ], i }
}
sort .Slice (q , func (i , j int ) bool { return q [i ].mi < q [j ].mi })
ans := make([]int , len(queries ))
trie := & Trie {}
idx := 0
for _ , v := range q {
for idx < len(nums ) && nums [idx ] <= v .mi {
trie .insert (nums [idx ])
idx ++
}
if idx > 0 {
ans [v .idx ] = trie .getMaxXor (v .xi )
} else {
ans [v .idx ] = - 1
}
}
return ans
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Trie {
Trie[] child = new Trie[ 2] ;
}
class Solution {
public int [] maximizeXor (int [] nums, int [][] queries) {
Arrays.sort (nums);
int n = queries.length ;
int [][] q = new int [ n][ 3] ;
for (int i = 0; i < n; i++ ) {
q[ i][ 0] = queries[ i][ 1] ;
q[ i][ 1] = queries[ i][ 0] ;
q[ i][ 2] = i;
}
Arrays.sort (q, Comparator.comparingInt (a -> a[ 0] ));
int [] ans = new int [ n] ;
Trie trie = new Trie();
int idx = 0;
for (int [] v : q) {
int mi = v[ 0] , xi = v[ 1] , i = v[ 2] ;
while (idx < nums.length && nums[ idx] <= mi) {
Trie node = trie;
for (int k = 30; k >= 0; k-- ) {
int b = (nums[ idx] >> k) & 1;
if (node.child [ b] == null ) node.child [ b] = new Trie();
node = node.child [ b] ;
}
idx++ ;
}
if (idx > 0) {
Trie node = trie;
int res = 0;
for (int k = 30; k >= 0; k-- ) {
int b = (xi >> k) & 1;
if (node.child [ 1 - b] != null ) {
res |= (1 << k);
node = node.child [ 1 - b] ;
} else if (node.child [ b] != null ) {
node = node.child [ b] ;
} else {
res = - 1;
break ;
}
}
ans[ i] = res;
} else {
ans[ i] = - 1;
}
}
return ans;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Trie {
val child = arrayOfNulls<Trie>(2 )
}
class Solution {
fun maximizeXor (nums: IntArray, queries: Array<IntArray>): IntArray {
nums.sort()
val n = queries.size
val q = Array(n) { IntArray(3 ) }
for (i in 0 until n) {
q[i][0 ] = queries[i][1 ]
q[i][1 ] = queries[i][0 ]
q[i][2 ] = i
}
q.sortBy { it [0 ] }
val ans = IntArray(n)
val trie = Trie()
var idx = 0
for (v in q) {
val mi = v[0 ]; val xi = v[1 ]; val i = v[2 ]
while (idx < nums.size && nums[idx] <= mi) {
var node = trie
for (k in 30 downTo 0 ) {
val b = (nums[idx] shr k) and 1
if (node.child[b] == null ) node.child[b] = Trie()
node = node.child[b]!!
}
idx++
}
if (idx > 0 ) {
var node = trie
var res = 0
for (k in 30 downTo 0 ) {
val b = (xi shr k) and 1
if (node.child[1 - b] != null ) {
res = res or (1 shl k)
node = node.child[1 - b]!!
} else if (node.child[b] != null ) {
node = node.child[b]!!
} else {
res = -1
break
}
}
ans[i] = res
} else {
ans[i] = -1
}
}
return ans
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def maximize_xor (nums: list[int], queries: list[list[int]]) -> list[int]:
class Trie :
def __init__ (self):
self. child = [None , None ]
def insert (root: Trie, num: int):
node = root
for i in range(30 , - 1 , - 1 ):
b = (num >> i) & 1
if not node. child[b]:
node. child[b] = Trie()
node = node. child[b]
def get_max_xor (root: Trie, num: int) -> int:
node = root
ans = 0
for i in range(30 , - 1 , - 1 ):
b = (num >> i) & 1
if node. child[1 - b]:
ans |= (1 << i)
node = node. child[1 - b]
elif node. child[b]:
node = node. child[b]
else :
return - 1
return ans
nums. sort()
q = sorted([(mi, xi, i) for i, (xi, mi) in enumerate(queries)])
ans = [- 1 ] * len(queries)
root = Trie()
idx = 0
for mi, xi, i in q:
while idx < len(nums) and nums[idx] <= mi:
insert(root, nums[idx])
idx += 1
if idx:
ans[i] = get_max_xor(root, xi)
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
struct Trie { child: [Option< Box< Trie>> ; 2 ] }
impl Trie {
fn new () -> Self { Trie { child: [None, None] } }
fn insert (& mut self, num: i32 ) {
let mut node = self;
for i in (0 ..= 30 ).rev() {
let b = (num >> i) & 1 ;
node = node.child[b as usize ].get_or_insert_with(|| Box::new(Trie::new()));
}
}
fn get_max_xor (& self, num: i32 ) -> i32 {
let mut node = self;
let mut ans = 0 ;
for i in (0 ..= 30 ).rev() {
let b = (num >> i) & 1 ;
if let Some(ref next) = node.child[(1 - b) as usize ] {
ans |= 1 << i;
node = next;
} else if let Some(ref next) = node.child[b as usize ] {
node = next;
} else {
return - 1 ;
}
}
ans
}
}
impl Solution {
pub fn maximize_xor (nums: Vec< i32 > , queries: Vec< Vec< i32 >> ) -> Vec< i32 > {
let mut nums = nums;
nums.sort();
let mut q: Vec< (i32 , i32 , usize )> = queries.iter().enumerate().map(| (i, v)| (v[1 ], v[0 ], i)).collect();
q.sort();
let mut ans = vec! [- 1 ; queries.len()];
let mut trie = Trie::new();
let mut idx = 0 ;
for (mi, xi, i) in q {
while idx < nums.len() && nums[idx] <= mi {
trie.insert(nums[idx]);
idx += 1 ;
}
if idx > 0 {
ans[i] = trie.get_max_xor(xi);
}
}
ans
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Trie {
child : [Trie ? , Trie ? ] = [undefined , undefined ];
}
class Solution {
maximizeXor (nums : number [], queries : number [][]): number [] {
nums .sort ((a , b ) => a - b );
const q = queries .map (([xi , mi ], i ) => [mi , xi , i ]).sort ((a , b ) => a [0 ] - b [0 ]);
const ans = Array(queries .length ).fill (- 1 );
const root = new Trie ();
let idx = 0 ;
function insert (num : number ) {
let node = root ;
for (let i = 30 ; i >= 0 ; -- i ) {
const b = (num >> i ) & 1 ;
if (! node .child [b ]) node .child [b ] = new Trie ();
node = node .child [b ]! ;
}
}
function getMaxXor (num : number ): number {
let node = root , ans = 0 ;
for (let i = 30 ; i >= 0 ; -- i ) {
const b = (num >> i ) & 1 ;
if (node .child [1 - b ]) {
ans |= (1 << i );
node = node .child [1 - b ]! ;
} else if (node .child [b ]) {
node = node .child [b ]! ;
} else {
return - 1 ;
}
}
return ans ;
}
for (const [mi , xi , i ] of q ) {
while (idx < nums .length && nums [idx ] <= mi ) insert (nums [idx ++ ]);
if (idx ) ans [i ] = getMaxXor (xi );
}
return ans ;
}
}
Complexity#
⏰ Time complexity: O((n + q) * 31)
, where n
is the length of nums
and q
is the number of queries. Each insert and query takes up to 31 steps for 32-bit numbers.
🧺 Space complexity: O(n * 31)
, for the Trie storing up to n
numbers.