Facebook Pixel

1829. Maximum XOR for Each Query

MediumBit ManipulationArrayPrefix Sum
Leetcode Link

Problem Description

You have a sorted array nums containing n non-negative integers and an integer maximumBit. You need to perform n queries on this array.

For each query:

  1. Find a non-negative integer k where k < 2^maximumBit such that the XOR of all elements in the current array with k gives the maximum possible value. In other words, find k that maximizes: nums[0] XOR nums[1] XOR ... XOR nums[nums.length-1] XOR k
  2. After finding k for the current query, remove the last element from the array before proceeding to the next query

The goal is to return an array answer where answer[i] contains the value of k found in the i-th query.

Key Points:

  • Each query operates on a progressively smaller array (removing the last element after each query)
  • The value k must be less than 2^maximumBit
  • You want to maximize the XOR result in each query
  • The process repeats exactly n times (once for each element originally in the array)

For example, if you start with nums = [0,1,2,3] and maximumBit = 2:

  • First query: Find k that maximizes XOR with [0,1,2,3], then remove 3
  • Second query: Find k that maximizes XOR with [0,1,2], then remove 2
  • Third query: Find k that maximizes XOR with [0,1], then remove 1
  • Fourth query: Find k that maximizes XOR with [0], then remove 0
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To maximize the XOR result, we need to understand how XOR works at the bit level. XOR gives us 1 when bits are different and 0 when bits are same. So to get the maximum value, we want as many 1s as possible in the higher bit positions.

Let's say we have the XOR of all current elements as xs. To maximize xs XOR k, we should make k have opposite bits to xs wherever possible. This way:

  • If xs has a 0 at position i, we set k to have 1 at position i → resulting in 0 XOR 1 = 1
  • If xs has a 1 at position i, we set k to have 0 at position i → resulting in 1 XOR 0 = 1

This gives us the maximum number of 1s in the result.

Since we're constrained by k < 2^maximumBit, we can only flip bits from position maximumBit-1 down to position 0. We examine each bit position from high to low: if the XOR sum has a 0 at that position, we set the corresponding bit in k to 1.

The clever optimization here is that instead of recalculating the XOR sum from scratch for each query, we can maintain a running XOR sum. When we "remove" the last element for the next query, we simply XOR the running sum with that element (since a XOR a = 0, this effectively removes it from the XOR chain).

By processing the array in reverse order and maintaining the XOR sum, we can efficiently find the optimal k for each query state without redundant calculations.

Learn more about Prefix Sum patterns.

Solution Approach

The implementation follows these steps:

  1. Calculate Initial XOR Sum: First, compute the XOR of all elements in nums and store it as xs. This represents nums[0] XOR nums[1] XOR ... XOR nums[n-1].

  2. Process Array in Reverse: Iterate through the array from the last element to the first. This aligns with the requirement to remove the last element after each query.

  3. Find Optimal k for Each Query:

    • For each iteration, initialize k = 0
    • Examine each bit position from maximumBit - 1 down to 0
    • Check if the bit at position i in xs is 0 using (xs >> i & 1) == 0
    • If it's 0, set the corresponding bit in k to 1 using k |= 1 << i
    • This ensures xs XOR k has a 1 at that bit position, maximizing the result
  4. Update XOR Sum: After finding k for the current state, update xs by XORing it with the current element x. This effectively "removes" x from the XOR sum since (a XOR b) XOR b = a.

  5. Build Answer Array: Append each k to the answer array and return it after processing all elements.

The algorithm processes each element once and checks maximumBit bits for each element, giving us a time complexity of O(n × maximumBit). The space complexity is O(1) excluding the output array.

The key insight is using the property that XOR is its own inverse: removing an element from an XOR sum is the same as XORing the sum with that element again.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with nums = [0,1,1,3] and maximumBit = 2.

Initial Setup:

  • First, calculate the XOR sum of all elements: xs = 0 XOR 1 XOR 1 XOR 3 = 3 (in binary: 11)
  • We'll process the array in reverse order: 3 → 1 → 1 → 0

Query 1 (array state: [0,1,1,3], xs = 3 which is 11 in binary):

  • We need to find k < 2^2 = 4 that maximizes 3 XOR k
  • Check bit positions from maximumBit - 1 = 1 down to 0:
    • Bit 1: xs has 1 at position 1 (11 → second bit is 1), so keep k's bit 1 as 0
    • Bit 0: xs has 1 at position 0 (11 → first bit is 1), so keep k's bit 0 as 0
  • Result: k = 0 (binary: 00)
  • Verify: 3 XOR 0 = 3 (this gives us 11 in binary)
  • Update xs for next query: xs = 3 XOR 3 = 0 (remove last element 3)

Query 2 (array state: [0,1,1], xs = 0 which is 00 in binary):

  • Find k that maximizes 0 XOR k
  • Check bit positions:
    • Bit 1: xs has 0 at position 1, so set k's bit 1 to 1
    • Bit 0: xs has 0 at position 0, so set k's bit 0 to 1
  • Result: k = 3 (binary: 11)
  • Verify: 0 XOR 3 = 3 (maximized)
  • Update xs: xs = 0 XOR 1 = 1 (remove element 1)

Query 3 (array state: [0,1], xs = 1 which is 01 in binary):

  • Find k that maximizes 1 XOR k
  • Check bit positions:
    • Bit 1: xs has 0 at position 1, so set k's bit 1 to 1
    • Bit 0: xs has 1 at position 0, so keep k's bit 0 as 0
  • Result: k = 2 (binary: 10)
  • Verify: 1 XOR 2 = 3 (gives us 11 in binary)
  • Update xs: xs = 1 XOR 1 = 0 (remove element 1)

Query 4 (array state: [0], xs = 0 which is 00 in binary):

  • Find k that maximizes 0 XOR k
  • Check bit positions:
    • Bit 1: xs has 0 at position 1, so set k's bit 1 to 1
    • Bit 0: xs has 0 at position 0, so set k's bit 0 to 1
  • Result: k = 3 (binary: 11)
  • Verify: 0 XOR 3 = 3 (maximized)

Final Answer: [0, 3, 2, 3]

The key insight demonstrated here is that we're essentially finding the bitwise complement of xs within the constraint of maximumBit. By processing in reverse and updating the XOR sum incrementally, we avoid recalculating the XOR from scratch for each query.

Solution Implementation

1from typing import List
2from functools import reduce
3from operator import xor
4
5class Solution:
6    def getMaximumXor(self, nums: List[int], maximumBit: int) -> List[int]:
7        """
8        Find the maximum XOR value for each query by removing elements from the end.
9      
10        Args:
11            nums: List of non-negative integers
12            maximumBit: Maximum number of bits to consider
13          
14        Returns:
15            List of k values that maximize XOR for each query
16        """
17        result = []
18      
19        # Calculate XOR of all elements in nums
20        cumulative_xor = reduce(xor, nums)
21      
22        # Process queries by removing elements from the end
23        for num in reversed(nums):
24            # Find k that maximizes cumulative_xor XOR k
25            # where k < 2^maximumBit
26            k = 0
27          
28            # Check each bit position from most significant to least
29            for bit_position in range(maximumBit - 1, -1, -1):
30                # If current bit in cumulative_xor is 0, set it to 1 in k
31                # This maximizes the XOR result
32                if (cumulative_xor >> bit_position & 1) == 0:
33                    k |= 1 << bit_position
34          
35            result.append(k)
36          
37            # Remove current element from cumulative XOR for next query
38            cumulative_xor ^= num
39      
40        return result
41
1class Solution {
2    public int[] getMaximumXor(int[] nums, int maximumBit) {
3        int n = nums.length;
4      
5        // Calculate XOR of all elements in the array
6        int xorSum = 0;
7        for (int num : nums) {
8            xorSum ^= num;
9        }
10      
11        // Result array to store maximum XOR values
12        int[] result = new int[n];
13      
14        // Process each query by removing elements from the end
15        for (int i = 0; i < n; i++) {
16            // Get the element to be removed (from the end of current array)
17            int elementToRemove = nums[n - i - 1];
18          
19            // Find k that maximizes XOR with current xorSum
20            // k must be less than 2^maximumBit
21            int k = 0;
22          
23            // Check each bit position from most significant to least significant
24            for (int bitPosition = maximumBit - 1; bitPosition >= 0; bitPosition--) {
25                // If the bit at current position in xorSum is 0,
26                // set the corresponding bit in k to 1 to maximize XOR
27                if (((xorSum >> bitPosition) & 1) == 0) {
28                    k |= (1 << bitPosition);
29                }
30                // If the bit is already 1, keeping k's bit as 0 maximizes XOR
31            }
32          
33            // Store the maximum XOR value for this query
34            result[i] = k;
35          
36            // Remove the last element from xorSum for next iteration
37            xorSum ^= elementToRemove;
38        }
39      
40        return result;
41    }
42}
43
1class Solution {
2public:
3    vector<int> getMaximumXor(vector<int>& nums, int maximumBit) {
4        // Calculate XOR of all elements in the array
5        int xorSum = 0;
6        for (int& num : nums) {
7            xorSum ^= num;
8        }
9      
10        int n = nums.size();
11        vector<int> result(n);
12      
13        // Process queries in reverse order (removing elements from the end)
14        for (int i = 0; i < n; ++i) {
15            // Get the element to be removed in this iteration
16            int elementToRemove = nums[n - i - 1];
17          
18            // Find k that maximizes (xorSum XOR k)
19            // k must be less than 2^maximumBit
20            int k = 0;
21          
22            // Check each bit position from most significant to least significant
23            for (int bitPos = maximumBit - 1; bitPos >= 0; --bitPos) {
24                // If the bit at position bitPos in xorSum is 0,
25                // set the corresponding bit in k to 1 to maximize XOR
26                if ((xorSum >> bitPos & 1) == 0) {
27                    k |= (1 << bitPos);
28                }
29            }
30          
31            // Store the result for this query
32            result[i] = k;
33          
34            // Remove the element from xorSum for the next iteration
35            xorSum ^= elementToRemove;
36        }
37      
38        return result;
39    }
40};
41
1/**
2 * Finds the maximum XOR values for each query by removing elements from the end
3 * @param nums - Array of non-negative integers
4 * @param maximumBit - Maximum number of bits to consider
5 * @returns Array of maximum XOR values for each query
6 */
7function getMaximumXor(nums: number[], maximumBit: number): number[] {
8    // Calculate XOR of all elements in the array
9    let cumulativeXor: number = 0;
10    for (const num of nums) {
11        cumulativeXor ^= num;
12    }
13  
14    const arrayLength: number = nums.length;
15    const result: number[] = new Array(arrayLength);
16  
17    // Process each query by removing elements from the end
18    for (let i = 0; i < arrayLength; i++) {
19        // Get the element to be removed (from the end of array)
20        const elementToRemove: number = nums[arrayLength - i - 1];
21      
22        // Find k that maximizes XOR with current cumulative XOR
23        let maxXorValue: number = 0;
24      
25        // Check each bit position from most significant to least significant
26        for (let bitPosition = maximumBit - 1; bitPosition >= 0; bitPosition--) {
27            // If bit at current position is 0, set it to 1 in k for maximum XOR
28            if (((cumulativeXor >> bitPosition) & 1) === 0) {
29                maxXorValue |= (1 << bitPosition);
30            }
31        }
32      
33        // Store the maximum XOR value for this query
34        result[i] = maxXorValue;
35      
36        // Remove the element from cumulative XOR for next iteration
37        cumulativeXor ^= elementToRemove;
38    }
39  
40    return result;
41}
42

Time and Space Complexity

The time complexity is O(n × m), where n is the length of the array nums and m is the value of maximumBit.

  • The initial XOR reduction of all elements in nums takes O(n) time.
  • The main loop iterates through all n elements of nums in reverse.
  • For each element, we have an inner loop that iterates maximumBit times (from maximumBit - 1 down to 0).
  • Inside the inner loop, bit operations (shift, AND, OR) are performed in O(1) time.
  • Therefore, the total time complexity is O(n) + O(n × m) = O(n × m).

The space complexity is O(1) when ignoring the space consumption of the answer array ans. The only extra space used is for:

  • The variable xs to store the running XOR result
  • The variable k to build each answer element
  • The loop variable i

All these variables use constant space regardless of input size.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Understanding of XOR Maximization

A common mistake is thinking that we need to try all possible values of k from 0 to 2^maximumBit - 1 to find the maximum XOR. This brute force approach would be inefficient and unnecessary.

Why it's wrong: Testing all possible k values would result in O(n × 2^maximumBit) time complexity, which becomes impractical for larger values of maximumBit.

Correct approach: To maximize XOR, we should set bits in k wherever the cumulative XOR has a 0 bit. This is because 0 XOR 1 = 1 and 1 XOR 0 = 1, both giving us 1 in the result, while 1 XOR 1 = 0 would give us 0.

2. Misunderstanding the Bit Constraint

Some might interpret k < 2^maximumBit as meaning we can use all bits up to position maximumBit (inclusive), leading to checking bit positions from maximumBit down to 0.

Why it's wrong: If maximumBit = 3, then k < 2^3 = 8, meaning k can be at most 7 (binary: 111). This uses bits at positions 0, 1, and 2 only. Checking bit position 3 would be incorrect.

Correct approach: Always iterate from maximumBit - 1 down to 0, not from maximumBit down to 0.

3. Inefficient XOR Calculation

Recalculating the XOR of remaining elements from scratch after each query:

# Wrong approach
for i in range(len(nums)):
    current_xor = 0
    for j in range(len(nums) - i):
        current_xor ^= nums[j]
    # Find k...

Why it's wrong: This results in O(n²) time complexity just for calculating XOR sums.

Correct approach: Use the XOR property that a XOR a = 0. We can maintain a running XOR and remove elements by XORing again with the element being removed.

4. Processing Order Confusion

Processing the array from beginning to end while trying to remove from the end:

# Wrong approach
cumulative_xor = reduce(xor, nums)
for i in range(len(nums)):
    # Find k for current cumulative_xor
    # Remove nums[i] - but we should remove from the end!
    cumulative_xor ^= nums[i]

Why it's wrong: The problem specifically states to remove the last element after each query, not the first.

Correct approach: Either iterate through reversed(nums) or use indices counting backward from len(nums) - 1 to 0.

5. Bit Manipulation Errors

Using incorrect bit operations when building k:

# Wrong approach
if (cumulative_xor >> bit_position & 1) == 0:
    k += 1 << bit_position  # Using addition instead of OR

Why it's wrong: Addition can cause carry-over effects if the bit is already set, though in this specific case it won't happen since we're only setting bits that aren't already set.

Correct approach: Use bitwise OR (k |= 1 << bit_position) to ensure we're setting individual bits without affecting others.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Depth first search is equivalent to which of the tree traversal order?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More