Facebook Pixel

2317. Maximum XOR After Operations

MediumBit ManipulationArrayMath
Leetcode Link

Problem Description

You have an array of integers nums (0-indexed). You can perform an operation on this array as many times as you want. In each operation, you:

  1. Choose any non-negative integer x
  2. Choose any index i in the array
  3. Update nums[i] to become nums[i] AND (nums[i] XOR x)

Here, AND is the bitwise AND operation and XOR is the bitwise XOR operation.

Your goal is to find the maximum possible bitwise XOR of all elements in the array after performing any number of these operations.

Understanding the Operation:

The operation nums[i] AND (nums[i] XOR x) allows you to selectively turn some 1 bits in nums[i] into 0 bits. Since you can choose any value for x, the expression nums[i] XOR x can produce any possible value. When you AND this result with the original nums[i], you essentially keep only the bits that were 1 in the original nums[i] and also 1 in the XOR result.

Key Insight:

For the XOR of all elements to be maximum, each bit position should contribute 1 to the final result if possible. A bit position contributes 1 to the XOR result when an odd number of elements have 1 in that position.

The crucial observation is that through the operations, you can only turn 1 bits into 0 bits (never 0 into 1). Therefore, if at least one element has a 1 in a particular bit position initially, that bit position can contribute 1 to the maximum XOR result.

The maximum XOR is achieved by taking the bitwise OR of all elements in the original array, which captures all bit positions where at least one element has a 1.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what the operation nums[i] AND (nums[i] XOR x) actually does to a number.

For any bit in nums[i]:

  • If the bit is 0, then 0 AND anything = 0, so it stays 0
  • If the bit is 1, then 1 AND (1 XOR x_bit) depends on what x_bit is:
    • If x_bit = 0: we get 1 AND (1 XOR 0) = 1 AND 1 = 1 (bit stays 1)
    • If x_bit = 1: we get 1 AND (1 XOR 1) = 1 AND 0 = 0 (bit becomes 0)

This means we can selectively turn any 1 bit into a 0 bit by choosing the appropriate x, but we can never turn a 0 bit into a 1 bit.

Now, let's think about the XOR of all elements. For XOR to be maximum, we want as many high-value bits set to 1 as possible in the result.

For a particular bit position across all numbers:

  • If that bit is 1 in an odd number of elements, the XOR will have 1 in that position
  • If that bit is 1 in an even number of elements, the XOR will have 0 in that position

Since we can turn 1s into 0s but not vice versa, the best strategy is:

  • For each bit position, if at least one element has a 1 there, we can manipulate the numbers so that exactly one element keeps the 1 while others have 0
  • This ensures that bit contributes 1 to the final XOR

The maximum possible value occurs when every bit position that has at least one 1 across all elements contributes a 1 to the final result. This is exactly what the bitwise OR operation gives us - it sets a bit to 1 if any of the numbers has 1 in that position.

Therefore, the answer is simply the bitwise OR of all elements in the original array.

Learn more about Math patterns.

Solution Approach

Based on our understanding that the maximum XOR is achieved by taking the bitwise OR of all elements, the implementation is straightforward.

The solution uses Python's reduce function along with the bitwise OR operator to combine all elements:

class Solution:
    def maximumXOR(self, nums: List[int]) -> int:
        return reduce(or_, nums)

Breaking down the implementation:

  1. reduce function: This is a functional programming tool that applies a binary operation cumulatively to items in an iterable, from left to right, to reduce the iterable to a single value.

  2. or_ operator: This is the bitwise OR operator from Python's operator module. It performs bitwise OR between two numbers.

  3. How it works:

    • reduce(or_, nums) takes the first two elements of nums, applies OR to them
    • Then takes that result and applies OR with the third element
    • Continues this process until all elements are processed
    • Returns the final result

Example walkthrough:

If nums = [3, 2, 4, 6]:

  • Binary representations: 3 = 011, 2 = 010, 4 = 100, 6 = 110
  • Step 1: 3 OR 2 = 011 OR 010 = 011
  • Step 2: 011 OR 4 = 011 OR 100 = 111
  • Step 3: 111 OR 6 = 111 OR 110 = 111
  • Result: 111 (which is 7 in decimal)

This gives us the maximum possible XOR because:

  • Bit position 0: At least one number (3) has this bit set
  • Bit position 1: At least one number (2, 3, 6) has this bit set
  • Bit position 2: At least one number (4, 6) has this bit set

All these bit positions can contribute 1 to the final XOR through appropriate operations, giving us the maximum value of 7.

Time Complexity: O(n) where n is the length of the array, as we iterate through each element once.

Space Complexity: O(1) as we only use a constant amount of extra space.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with nums = [1, 2, 3] to understand how we achieve the maximum XOR.

Initial Setup:

  • 1 = 001 (binary)
  • 2 = 010 (binary)
  • 3 = 011 (binary)

Understanding what operations can do:

For nums[0] = 1 = 001:

  • We can keep it as 001 by choosing x = 0
  • Or turn it into 000 by choosing x = 1 (since 001 AND (001 XOR 001) = 001 AND 000 = 000)

For nums[1] = 2 = 010:

  • We can keep it as 010 by choosing x = 0
  • Or turn it into 000 by choosing x = 2 (since 010 AND (010 XOR 010) = 010 AND 000 = 000)

For nums[2] = 3 = 011:

  • We can keep it as 011, or selectively turn off bits
  • For example, turn it into 010 by choosing x = 1 (since 011 AND (011 XOR 001) = 011 AND 010 = 010)
  • Or turn it into 001 by choosing x = 2 (since 011 AND (011 XOR 010) = 011 AND 001 = 001)

Finding the maximum XOR:

To maximize XOR, we want each bit position to contribute 1 if possible:

  • Bit position 0: Present in nums[0]=1 and nums[2]=3. We can make exactly one element have this bit.
  • Bit position 1: Present in nums[1]=2 and nums[2]=3. We can make exactly one element have this bit.

One optimal configuration:

  • Transform nums[0] = 1 to 000 (using operation with x=1)
  • Keep nums[1] = 2 as 010
  • Transform nums[2] = 3 to 001 (using operation with x=2)
  • Result array: [000, 010, 001]
  • XOR: 000 XOR 010 XOR 001 = 011 = 3

Using the OR approach:

  • 1 OR 2 OR 3 = 001 OR 010 OR 011 = 011 = 3

The OR operation captures all bit positions where at least one element has a 1, which equals our maximum achievable XOR. This confirms that the maximum XOR is indeed the bitwise OR of all original elements.

Solution Implementation

1class Solution:
2    def maximumXOR(self, nums: List[int]) -> int:
3        """
4        Find the maximum XOR value that can be obtained from any subset of nums.
5      
6        Key insight: For any bit position, if at least one number in nums has that bit set to 1,
7        we can always include that bit in our maximum XOR result by choosing appropriate subset.
8        Therefore, the maximum XOR is simply the bitwise OR of all numbers.
9      
10        Args:
11            nums: List of non-negative integers
12          
13        Returns:
14            Maximum possible XOR value from any subset of nums
15        """
16        # Initialize result with 0
17        result = 0
18      
19        # Perform bitwise OR operation on all numbers
20        # This gives us all the bits that appear as 1 in at least one number
21        for num in nums:
22            result |= num
23          
24        return result
25
1class Solution {
2    /**
3     * Finds the maximum XOR value that can be obtained from any subset of the array.
4     * 
5     * The key insight is that for any bit position, if at least one number in the array
6     * has that bit set to 1, we can always include it in our result. This is because
7     * XOR of any subset can achieve this by including that number.
8     * 
9     * Using bitwise OR on all numbers gives us all the bits that appear as 1 
10     * in at least one number, which is exactly the maximum XOR we can achieve.
11     * 
12     * @param nums the input array of integers
13     * @return the maximum XOR value possible from any subset
14     */
15    public int maximumXOR(int[] nums) {
16        // Initialize result to store the maximum XOR value
17        int maxXorValue = 0;
18      
19        // Iterate through each number in the array
20        for (int currentNumber : nums) {
21            // Accumulate all set bits using bitwise OR operation
22            // This collects all bits that are 1 in at least one number
23            maxXorValue |= currentNumber;
24        }
25      
26        // Return the maximum XOR value
27        return maxXorValue;
28    }
29}
30
1class Solution {
2public:
3    int maximumXOR(vector<int>& nums) {
4        // Initialize the result variable to store the maximum XOR value
5        int result = 0;
6      
7        // Iterate through each number in the input array
8        // The key insight: OR operation accumulates all set bits across all numbers
9        // This gives us the maximum possible XOR value we can achieve
10        for (int& num : nums) {
11            // Perform bitwise OR to collect all set bits from all numbers
12            // Any bit that is 1 in any number will be 1 in the result
13            result |= num;
14        }
15      
16        // Return the maximum XOR value
17        // This works because we can always arrange elements to achieve this XOR
18        return result;
19    }
20};
21
1/**
2 * Finds the maximum XOR value that can be obtained by selecting any subset of the given array
3 * The maximum XOR is achieved by taking the bitwise OR of all elements
4 * 
5 * @param nums - Array of non-negative integers
6 * @returns The maximum XOR value possible
7 */
8function maximumXOR(nums: number[]): number {
9    // Initialize result to store the maximum XOR value
10    let maxXorValue: number = 0;
11  
12    // Iterate through each number in the array
13    for (const currentNumber of nums) {
14        // Accumulate all set bits using bitwise OR operation
15        // This gives us the maximum possible XOR value
16        maxXorValue |= currentNumber;
17    }
18  
19    // Return the maximum XOR value
20    return maxXorValue;
21}
22

Time and Space Complexity

Time Complexity: O(n), where n is the length of the array nums. The reduce function with the or_ operator iterates through all elements in the array exactly once, performing a bitwise OR operation between each element and the accumulated result. Since each bitwise OR operation takes O(1) time, and we perform n-1 such operations for an array of length n, the total time complexity is O(n).

Space Complexity: O(1). The reduce function only maintains a single accumulator variable to store the intermediate result of the bitwise OR operations. No additional data structures are created that scale with the input size. The space used remains constant regardless of the length of the input array.

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

Common Pitfalls

Pitfall 1: Misunderstanding the Problem as Finding Maximum XOR of a Subset

The Mistake: Many people initially interpret this problem as finding the maximum XOR value among all possible subsets of the array, which would require checking combinations like:

  • XOR of pairs: nums[i] XOR nums[j]
  • XOR of triplets: nums[i] XOR nums[j] XOR nums[k]
  • And so on...

This misinterpretation leads to complex solutions involving tries or dynamic programming to find the maximum XOR subset.

Why This Happens: The problem statement mentions "maximum possible bitwise XOR of all elements in the array after performing operations," which can be confusing. The key detail that's easy to miss is that the operations modify individual array elements before computing the XOR.

The Correct Understanding: The operations allow you to modify each element by turning some of its 1 bits to 0. After all modifications, you compute the XOR of ALL modified elements (not a subset). The goal is to strategically modify elements so their collective XOR is maximized.

Solution: Focus on what the operation nums[i] AND (nums[i] XOR x) actually does - it can only turn 1 bits to 0, never create new 1 bits. This constraint is crucial for understanding why the answer is simply the OR of all elements.

Pitfall 2: Overcomplicating the Operation Analysis

The Mistake: Trying to enumerate all possible values of x and all possible transformations for each element, leading to exponential complexity considerations.

# Incorrect approach - trying to find optimal x values
def maximumXOR(nums):
    n = len(nums)
    max_xor = 0
    # Try different combinations of x values for each element
    for mask in range(1 << 30):  # Trying different x values
        temp_nums = nums[:]
        for i in range(n):
            # Apply operation with current x
            temp_nums[i] = temp_nums[i] & (temp_nums[i] ^ mask)
        # Calculate XOR of modified array
        current_xor = 0
        for num in temp_nums:
            current_xor ^= num
        max_xor = max(max_xor, current_xor)
    return max_xor

Why This Happens: The freedom to choose any x value makes it seem like we need to explore many possibilities.

The Correct Approach: Recognize that for any bit position, if at least one number has that bit set, we can always arrange the operations to make exactly one number retain that bit (turning it off in all others), thus contributing 1 to the final XOR. This leads directly to the OR solution.

Pitfall 3: Incorrect Implementation Using XOR Instead of OR

The Mistake: Confusing the final operation and using XOR to combine all elements:

# Incorrect implementation
def maximumXOR(nums):
    result = 0
    for num in nums:
        result ^= num  # Wrong! This gives current XOR, not maximum possible
    return result

Why This Happens: Since the problem asks for maximum XOR, it's natural to think about using the XOR operation in the solution.

The Fix: Remember that we need OR to collect all possible 1 bits that exist across all numbers:

# Correct implementation
def maximumXOR(nums):
    result = 0
    for num in nums:
        result |= num  # Correct! Collects all 1 bits
    return result

Pitfall 4: Edge Case Handling

The Mistake: Not considering edge cases like:

  • Empty array (though typically problems specify non-empty arrays)
  • Array with single element
  • Array with all zeros

Solution: Ensure your implementation handles these cases:

def maximumXOR(nums):
    if not nums:  # Handle empty array if needed
        return 0
  
    result = 0
    for num in nums:
        result |= num
    return result
    # This naturally handles single element and all-zeros cases correctly
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?


Recommended Readings

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

Load More