Problem Overview
LeetCode 1829 — Maximum XOR for Each Query — gives you a sorted integer array nums and a positive integer maximumBit k. You must answer n queries, where query i asks: what value x in [0, 2^k - 1] maximizes the XOR of x with all elements currently in nums? After answering each query, the last element of nums is removed.
The result array ans has the same length as nums. ans[0] corresponds to using all elements, ans[1] corresponds to all elements except the last one, and so on. Because k is bounded, x is a k-bit non-negative integer — choosing x optimally always requires inspecting every bit of the running XOR in the lower k positions.
This problem sits at the intersection of prefix XOR and bitwise complement techniques. The key insight is that XOR of all current elements is easy to maintain incrementally, and maximizing XOR with a fixed range [0, 2^k - 1] has a clean closed-form answer.
- Input: sorted array nums (1 <= nums[i] < 2^k) and integer maximumBit k
- Output: array of n answers, one per query in order
- Query i: find x in [0, 2^k-1] maximizing x XOR (nums[0] XOR nums[1] XOR ... XOR nums[n-1-i])
- After each query, remove the last element of the current array
- Constraints: 1 <= nums.length <= 10^5; 1 <= k <= 20; 0 <= nums[i] < 2^k
Running XOR and Complement
XOR all elements of the current array gives a value called xorAll. To maximize xorAll XOR x where x can be any k-bit number, you want every bit of x to be the opposite of the corresponding bit of xorAll in the lower k positions. This is precisely the bitwise complement of xorAll restricted to k bits.
The k-bit complement of xorAll is computed as xorAll XOR mask, where mask = (1 << k) - 1. The mask has exactly k ones in its lower bits, so XOR with the mask flips all k lower bits of xorAll and leaves higher bits unaffected. This one operation gives the optimal x for any query in O(1).
Because nums[i] < 2^k by constraint, the XOR of all elements also fits in k bits. The higher bits of xorAll are always zero, so XOR with mask also zeroes out the higher bits of the result, keeping x in the valid range [0, 2^k - 1].
Maximizing a XOR b in k Bits
To maximize a XOR b where b is in [0, 2^k - 1]: set every bit of b to the opposite of a's bit in the lower k positions. This is exactly b = a XOR (2^k - 1) — flip all k lower bits of a using the mask.
Algorithm
First compute xorAll as the XOR of all elements in nums. Then build the mask as (1 << k) - 1. The first answer is xorAll XOR mask. Then iterate from the last element of nums backwards: XOR it out of xorAll and compute the next answer as xorAll XOR mask. This fills the answer array from index 0 to n-1 in one forward pass.
The entire algorithm is O(n) time and O(n) space (for the result). The initial XOR computation is O(n), and each of the n query steps is O(1). No sorting or additional data structures are required beyond the result array.
Because we process elements from back to front, xorAll naturally shrinks by removing nums[n-1], then nums[n-2], and so on — matching exactly the order in which the problem removes elements.
- 1Step 1: Compute xorAll = nums[0] XOR nums[1] XOR ... XOR nums[n-1]
- 2Step 2: Compute mask = (1 << k) - 1
- 3Step 3: ans[0] = xorAll XOR mask (answer for all elements)
- 4Step 4: For i from n-1 down to 1: xorAll ^= nums[i]; ans[n-i] = xorAll XOR mask
- 5Step 5: Return ans
Why Process from the Back
The problem specifies that each query removes the last element of the remaining array. So query 0 uses all n elements, query 1 uses the first n-1 elements (removing nums[n-1]), query 2 uses the first n-2 elements, and so on. The removal proceeds from right to left in the original array.
Processing from the back perfectly matches this removal order. Starting with the full XOR, we XOR out nums[n-1] to get the prefix XOR for query 1, then XOR out nums[n-2] for query 2, etc. Each XOR-out is O(1) because XOR is its own inverse: a XOR a = 0.
Alternatively, one could precompute prefix XOR values from left to right and index them directly. Both approaches are O(n), but the backwards loop avoids building the prefix array entirely and is a single clean loop.
XOR Removal is Exact
The problem says "remove the last element" each query; this means the XOR shrinks from the right. Build results forward by XORing out the last element each step — since a XOR a = 0, removing an element from a running XOR is as simple as XORing it in again.
Walk-Through Example
Let nums = [0, 1, 1, 3] and k = 2. First compute mask = (1 << 2) - 1 = 3 (binary 11). Compute xorAll = 0 XOR 1 XOR 1 XOR 3 = 3 (binary 11). Query 0 (all elements): ans[0] = 3 XOR 3 = 0. Remove nums[3] = 3: xorAll = 3 XOR 3 = 0. Query 1: ans[1] = 0 XOR 3 = 3.
Remove nums[2] = 1: xorAll = 0 XOR 1 = 1. Query 2: ans[2] = 1 XOR 3 = 2. Remove nums[1] = 1: xorAll = 1 XOR 1 = 0. Query 3: ans[3] = 0 XOR 3 = 3. Final result: [0, 3, 2, 3].
Verify query 0: XOR(0,1,1,3) = 3; best x = 0 since 3 XOR 0 = 3, but 3 XOR 0 = 3 and 3 XOR 3 = 0 — wait, we want to maximize: ans[0]=0 means x=0 gives 3 XOR 0=3, while x=3 gives 3 XOR 3=0. So best x=0 giving XOR=3. Correct.
- 1nums=[0,1,1,3], k=2, mask=3 (binary 11)
- 2xorAll = 0^1^1^3 = 3 (binary 11)
- 3ans[0] = 3 XOR 3 = 0 (x=0 maximizes XOR: 3^0=3)
- 4Remove 3: xorAll = 3^3 = 0; ans[1] = 0 XOR 3 = 3 (x=3 maximizes: 0^3=3)
- 5Remove 1: xorAll = 0^1 = 1; ans[2] = 1 XOR 3 = 2 (x=2 maximizes: 1^2=3)
- 6Remove 1: xorAll = 1^1 = 0; ans[3] = 0 XOR 3 = 3 (x=3 maximizes: 0^3=3)
- 7Result = [0, 3, 2, 3]
Code Walkthrough — Python and Java
Python: from functools import reduce; from operator import xor; def getMaximumXor(nums, maximumBit): mask = (1 << maximumBit) - 1; xorAll = reduce(xor, nums); ans = []; [ans.append(xorAll ^ mask) or setattr(__builtins__, "_", xorAll := xorAll ^ nums[i]) for i in range(len(nums)-1, -1, -1)]. Cleaner version: compute xorAll, then loop from back appending xorAll^mask and XORing out each element. O(n) time, O(n) space.
Java: public int[] getMaximumXor(int[] nums, int maximumBit) { int n = nums.length; int mask = (1 << maximumBit) - 1; int xorAll = 0; for (int x : nums) xorAll ^= x; int[] ans = new int[n]; for (int i = 0; i < n; i++) { ans[i] = xorAll ^ mask; xorAll ^= nums[n - 1 - i]; } return ans; }. Forward loop from i=0 to n-1 fills ans in order, XORing out the last element each step.
Both implementations avoid nested loops. The Python reduce initializes xorAll in one line; the Java version uses an explicit loop for clarity. Space is O(n) for the output array only — no auxiliary data structure is needed.
Do Not Recompute XOR from Scratch
Don't recompute XOR from scratch for each query — that would be O(n^2) overall. Instead, maintain xorAll incrementally: XOR out the last element in O(1) per step. This keeps the total time O(n).