## Title Description

This is "1707. The maximum XOR value of the element in the array" on LeetCode. The difficulty is "difficult".

Tag: "persistent dictionary tree", "binary"

Give you an array of nonnegative integers

nums. There is another query array

queries, where

queries[i] = [xi, mi].

The first

iThe answer to a query is

x_iAnd any

numsNo more than

m_iThe maximum value obtained by bitwise XOR (XOR) of the element.

In other words, the answer is max (Num [J] \ {XOR} x_i), where all

jAll meet

nums[j] <= m_i. If

numsAll elements in are greater than

m_i, the final answer is

-1.

Returns an array of integers

answerAs the answer to the query, where

answer.length == queries.lengthAnd

answer[i]Yes

iAnswer to a query.

Example 1:

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 that do not exceed 1. 0 XOR 3 = 3 And 1 XOR 3 = 2 . The greater of the two is 3. 2) 1 XOR 2 = 3. 3) 5 XOR 2 = 7.

Example 2:

Input: nums = [5,2,4,6,6,3], queries = [[12,4],[8,1],[6,3]] Output:[15,-1,5]

Tips:

- 1 <= nums.length, queries.length <=

- queries[i].length == 2
- 0 <= nums[j], xi, mi <=

## Basic analysis

Please make sure that you have finished before doing this question 421. Maximum XOR value of two numbers in the array.

Given all the questions in advance, we can use the offline idea (adjust the answer order of the questions) to solve them.

There are two offline ways to solve this problem.

## Ordinary Trie

The basic idea of the first method is: do not put all the numbers at one time, but put the numbers that need to participate in the screening every time

Trie, and then 421. Maximum XOR value of two numbers in the array Similar greedy search logic.

Specifically, we can handle it according to the following logic:

- Sort nums from small to large, and sort the second dimension of queries from small to large (save the original subscript mapping relationship before sorting).
- Process all queries in sort order [i]:
- Before answering each query, store a value less than or equal to queries[i][1]

. Since we have sorted nums in advance, this process only needs to maintain a pointer that moves to the right on nums.

- Then, using the greedy idea, query the maximum value that can be found in each query [i] [0], and calculate the XOR and (this process is similar to 421. Maximum XOR value of two numbers in the array Consistent).
- Find the subscript of the current query in the original query sequence and save the answer.

code:

class Solution { static int N = (int)1e5 * 32; static int[][] trie = new int[N][2]; static int idx = 0; public Solution() { for (int i = 0; i <= idx; i++) { Arrays.fill(trie[i], 0); } idx = 0; } void add(int x) { int p = 0; for (int i = 31; i >= 0; i--) { int u = (x >> i) & 1; if (trie[p][u] == 0) trie[p][u] = ++idx; p = trie[p][u]; } } int getVal(int x) { int ans = 0; int p = 0; for (int i = 31; i >= 0; i--) { int a = (x >> i) & 1, b = 1 - a; if (trie[p][b] != 0) { p = trie[p][b]; ans = ans | (b << i); } else { p = trie[p][a]; ans = ans | (a << i); } } return ans ^ x; } public int[] maximizeXor(int[] nums, int[][] qs) { int m = nums.length, n = qs.length; // Use a hash table to save the original order Map<int[], Integer> map = new HashMap<>(); for (int i = 0; i < n; i++) map.put(qs[i], i); // Sort nums and queries[x][1] from small to large Arrays.sort(nums); Arrays.sort(qs, (a, b)->a[1]-b[1]); int[] ans = new int[n]; int loc = 0; // Record which positions in num have been put into Trie for (int[] q : qs) { int x = q[0], limit = q[1]; // Store the number less than or equal to limit in Trie while (loc < m && nums[loc] <= limit) add(nums[loc++]); if (loc == 0) { ans[map.get(q)] = -1; } else { ans[map.get(q)] = getVal(x); } } return ans; } }

- Time complexity: let the length of nums be m and the length of qs be n. The sorting complexity is

and

O(n\log{n})； Insert all numbers

TrieAnd from

TrieThe complexity of finding in is

O(Len)，

Lenby

32. The overall complexity is

O(m\log{m} + n\log{n} + (m + n) * Len)=

O(m * \max(\log{m}, Len) + n * \max(\log{n}, Len)).

- Space complexity:

. among

CIs constant, fixed to

1e5 * 32 * 2.

## Count trie & two points

Another way to "increase the difficulty" is to turn the whole process over: deposit all the information at one time

TrieAnd then change the number of no longer participating from

TrieRemove from. Compared with solution one, this requires us to

TrieAdd a "delete / count" function, and need to implement dichotomy to find the upper bound subscript of the removed element.

Specifically, we can handle it according to the following logic:

- Sort nums from large to small, and sort the second dimension of queries from large to small (save the original subscript mapping of the query before sorting).
- Process all queries in sort order [i]:
- Before answering each query, find the first subscript in nums that satisfies "less than or equal to queries[i][1] through" bisection ", and then change the number before the subscript from

Remove from. Similarly, in this process, we need to use a pointer to record the subscript position of the last deletion to avoid repeated deletion.

- Then use the greedy idea to query the maximum value that each query [i] [0] can find. Note that this is to judge whether the current node has been counted. If not, it returns

.

- Find the subscript of the current query in the original query sequence and save the answer.

code:

class Solution { static int N = (int)1e5 * 32; static int[][] trie = new int[N][2]; static int[] cnt = new int[N]; static int idx = 0; public Solution() { for (int i = 0; i <= idx; i++) { Arrays.fill(trie[i], 0); cnt[i] = 0; } idx = 0; } // Deposit (v = 1) / delete (v = -1) a number x to Trie void add(int x, int v) { int p = 0; for (int i = 31; i >= 0; i--) { int u = (x >> i) & 1; if (trie[p][u] == 0) trie[p][u] = ++idx; p = trie[p][u]; cnt[p] += v; } } int getVal(int x) { int ans = 0; int p = 0; for (int i = 31; i >= 0; i--) { int a = (x >> i) & 1, b = 1 - a; if (cnt[trie[p][b]] != 0) { p = trie[p][b]; ans = ans | (b << i); } else if (cnt[trie[p][a]] != 0) { p = trie[p][a]; ans = ans | (a << i); } else { return -1; } } return ans ^ x; } public int[] maximizeXor(int[] nums, int[][] qs) { int n = qs.length; // Use a hash table to save the original order Map<int[], Integer> map = new HashMap<>(); for (int i = 0; i < n; i++) map.put(qs[i], i); // In descending order sort(nums); Arrays.sort(qs, (a, b)->b[1]-a[1]); // Store all data in Trie for (int i : nums) add(i, 1); int[] ans = new int[n]; int left = -1; // In nums, the values subscript "less than or equal to" left have been removed from Trie for (int[] q : qs) { int x = q[0], limit = q[1]; // Binary finds the right boundary of the element to be deleted and removes all values before its right boundary from Trie. int right = getRight(nums, limit); for (int i = left + 1; i < right; i++) add(nums[i], -1); left = right - 1; ans[map.get(q)] = getVal(x); } return ans; } // Find the right boundary to be deleted int getRight(int[] nums, int limit) { int l = 0, r = nums.length - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] <= limit) { r = mid; } else { l = mid + 1; } } return nums[r] <= limit ? r : r + 1; } // Sort nums in descending order (Java does not have Api to directly support the reverse order of basic type int, which can be ignored in other languages) void sort(int[] nums) { Arrays.sort(nums); int l = 0, r = nums.length - 1; while (l < r) { int c = nums[r]; nums[r--] = nums[l]; nums[l++] = c; } } }

- Time complexity: let the length of nums be m and the length of qs be n, constant

. The sorting complexity is

O(m\log{m})and

O(n\log{n})； Insert all numbers

TrieThe complexity of is

O(m * Len)； Each query needs to go through "dichotomy" to find the boundary, and the complexity is

O(n\log{m})； In the worst case, all numbers will start from

TrieIs marked for deletion with complexity of

O(m * Len). The overall complexity is

O(m\log{m} + n\log{n} + n\log{m} + mLen)=

O(m * \max(\log{m}, Len) + n * \max(\log{m}, \log{n})).

- Space complexity:

. among

CIs constant, fixed to

1e5 * 32 * 3.

## explain

Both of these methods adopt "array implementation", and because of the large data range, static is used to optimize the creation of large arrays. The specific "optimization reason" and "class implementation Trie method" can be found in the problem solution 208. Implement Trie (prefix tree) Check. I won't repeat it here.

## last

This is No.1707 of our "brush through LeetCode" series. The series began on January 1, 2021. As of the starting date, there are 1916 questions on LeetCode, some of which are locked questions. We will brush all the questions without locks first.

In this series of articles, in addition to explaining the problem-solving ideas, we will also give the most concise code as far as possible. If the general explanation is involved, the corresponding code template will also be.

In order to facilitate students to debug and submit code on the computer, I have established a relevant warehouse: https://github.com/SharingSource/LogicStack-LeetCode .

In the warehouse address, you can see the problem solution link of the series of articles, the corresponding code of the series of articles, the original problem link of LeetCode and other preferred problem solutions.