LeetCode 2317 - Maximum XOR After Operations
The problem gives us an integer array nums, and we may perform a special operation any number of times. In one operation, we choose an index i and any non-negative integer x, then replace: Our goal is to maximize the bitwise XOR of all elements in the array after performing as…
Difficulty: 🟡 Medium
Topics: Array, Math, Bit Manipulation
Solution
Problem Understanding
The problem gives us an integer array nums, and we may perform a special operation any number of times. In one operation, we choose an index i and any non-negative integer x, then replace:
nums[i] = nums[i] AND (nums[i] XOR x)
Our goal is to maximize the bitwise XOR of all elements in the array after performing as many operations as we want.
The key challenge is understanding what this operation actually allows us to do to each number. At first glance, the expression looks complicated because it combines both XOR and AND. However, once we analyze the bit behavior carefully, the problem becomes much simpler.
The input size can be very large:
nums.lengthcan be up to10^5- Each number can be as large as
10^8
These constraints immediately rule out any brute-force simulation of operations. Since there are infinitely many choices for x, trying all operations directly is impossible. We need to derive a mathematical property of the operation instead.
An important observation is that the operation only removes bits from a number, it never creates new 1 bits. Therefore, every number can only lose bits over time.
Some important edge cases include:
- Arrays containing only zeros
- Arrays with a single element
- Numbers with completely disjoint bit patterns
- Numbers sharing many overlapping bits
- Very large arrays where efficiency matters
The problem guarantees all numbers are non-negative, so we only deal with standard binary bit manipulation without worrying about sign bits.
Approaches
Brute Force Approach
A brute-force strategy would attempt to simulate operations on the array. For every index, we could try many possible values of x, generate new array states, compute the resulting XOR, and search for the maximum possible answer.
This approach is theoretically correct because it explores reachable states. However, it is completely impractical.
The problem is that:
xcan be any non-negative integer- The number of possible operations is enormous
- The state space grows exponentially
- Arrays can contain up to
100000elements
Even for very small arrays, exhaustive exploration becomes infeasible.
The brute-force approach fails because the operation space is effectively infinite.
Optimal Approach
The key insight comes from analyzing the operation bit by bit.
Consider a single bit position in nums[i].
If a bit in nums[i] is already 0, the result after the operation will always remain 0.
If a bit in nums[i] is 1, then:
- We can choose
xso that(nums[i] XOR x)has either0or1at that position - Since the final operation is an AND with the original bit
1, we can keep the bit as1or turn it into0
Therefore, the operation allows us to independently remove any set bit from a number.
This means each number can be transformed into any subset of its original bits.
Now consider the final XOR.
A bit position can appear in the final XOR if at least one number originally contains that bit. Since we can selectively remove bits from numbers, we can always arrange for a bit to contribute to the final XOR whenever it exists somewhere in the array.
That means the maximum possible XOR simply contains every bit that appears in at least one number.
This is exactly the bitwise OR of all elements.
So the answer is:
nums[0] OR nums[1] OR nums[2] OR ...
This transforms the problem into a simple linear scan.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | Exponential | Exponential | Explores operation states, infeasible |
| Optimal | O(n) | O(1) | Compute bitwise OR of all elements |
Algorithm Walkthrough
- Initialize a variable called
resultto0.
This variable will accumulate every bit that appears anywhere in the array.
2. Iterate through each number in nums.
For every number, combine it with result using bitwise OR.
3. Update:
result = result OR current_number
The OR operation ensures that every bit appearing in any element becomes part of the final answer.
4. Continue until all elements have been processed.
5. Return result.
This value represents the maximum achievable XOR after any number of operations.
Why it works
The operation allows us to remove bits from individual numbers but never add new bits. Therefore, a bit can only appear in the final XOR if that bit existed in at least one original number.
Since we can selectively preserve bits to make their XOR contribution odd, every existing bit can be made to appear in the final XOR. Thus, the maximum achievable XOR contains all bits that appear anywhere in the array, which is exactly the bitwise OR of all numbers.
Python Solution
from typing import List
class Solution:
def maximumXOR(self, nums: List[int]) -> int:
result = 0
for num in nums:
result |= num
return result
The implementation is extremely concise because the mathematical observation eliminates the need for simulation.
The variable result starts at 0. As we iterate through the array, we continuously apply the bitwise OR operator.
The expression:
result |= num
is shorthand for:
result = result | num
This accumulates every bit that appears in any number.
At the end of the loop, result contains the union of all set bits across the array, which is the maximum achievable XOR.
Go Solution
func maximumXOR(nums []int) int {
result := 0
for _, num := range nums {
result |= num
}
return result
}
The Go implementation follows exactly the same logic as the Python version.
Go uses the |= operator for bitwise OR assignment, just like Python.
Since the constraints fit comfortably within Go's int type, there are no overflow concerns. The solution also uses constant extra memory because only a single accumulator variable is maintained.
Worked Examples
Example 1
Input:
nums = [3,2,4,6]
Binary representations:
| Number | Binary |
|---|---|
| 3 | 011 |
| 2 | 010 |
| 4 | 100 |
| 6 | 110 |
We compute the cumulative OR step by step.
| Step | Current Number | Result Before | Result After |
|---|---|---|---|
| 1 | 3 (011) | 000 | 011 |
| 2 | 2 (010) | 011 | 011 |
| 3 | 4 (100) | 011 | 111 |
| 4 | 6 (110) | 111 | 111 |
Final result:
111 binary = 7
Answer:
7
Example 2
Input:
nums = [1,2,3,9,2]
Binary representations:
| Number | Binary |
|---|---|
| 1 | 0001 |
| 2 | 0010 |
| 3 | 0011 |
| 9 | 1001 |
| 2 | 0010 |
Cumulative OR process:
| Step | Current Number | Result Before | Result After |
|---|---|---|---|
| 1 | 1 | 0000 | 0001 |
| 2 | 2 | 0001 | 0011 |
| 3 | 3 | 0011 | 0011 |
| 4 | 9 | 0011 | 1011 |
| 5 | 2 | 1011 | 1011 |
Final binary value:
1011 binary = 11
Answer:
11
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each element is processed exactly once |
| Space | O(1) | Only one accumulator variable is used |
The algorithm performs a single pass through the array. Each iteration executes one constant-time bitwise OR operation.
No additional data structures proportional to input size are allocated, so the extra space usage remains constant.
Test Cases
from typing import List
class Solution:
def maximumXOR(self, nums: List[int]) -> int:
result = 0
for num in nums:
result |= num
return result
solution = Solution()
assert solution.maximumXOR([3, 2, 4, 6]) == 7 # provided example
assert solution.maximumXOR([1, 2, 3, 9, 2]) == 11 # provided example
assert solution.maximumXOR([0]) == 0 # single zero
assert solution.maximumXOR([5]) == 5 # single element
assert solution.maximumXOR([0, 0, 0]) == 0 # all zeros
assert solution.maximumXOR([1, 2, 4, 8]) == 15 # disjoint bits
assert solution.maximumXOR([7, 7, 7]) == 7 # identical numbers
assert solution.maximumXOR([1, 1, 1, 1]) == 1 # repeated single bit
assert solution.maximumXOR([8, 1]) == 9 # separated high and low bits
assert solution.maximumXOR([10, 5]) == 15 # complementary bits
assert solution.maximumXOR([100000000]) == 100000000 # large value
assert solution.maximumXOR([0, 100000000]) == 100000000 # zero plus large value
assert solution.maximumXOR([6, 10, 15]) == 15 # overlapping bits
| Test | Why |
|---|---|
[3,2,4,6] |
Verifies provided example |
[1,2,3,9,2] |
Verifies second example |
[0] |
Smallest possible value |
[5] |
Single-element array |
[0,0,0] |
All bits absent |
[1,2,4,8] |
Every number contributes unique bits |
[7,7,7] |
Repeated identical values |
[1,1,1,1] |
Duplicate single-bit numbers |
[8,1] |
Combines distant bit positions |
[10,5] |
Complementary bit patterns |
[100000000] |
Large integer boundary |
[0,100000000] |
Large value with zero |
[6,10,15] |
Heavy bit overlap |
Edge Cases
One important edge case is an array containing only zeros. Since no number contains any set bits, the bitwise OR remains zero. A buggy implementation might incorrectly assume operations can create new bits, but the operation only removes bits. The implementation handles this naturally because OR-ing zeros always produces zero.
Another important case is a single-element array. Since the final XOR equals the single remaining value, the maximum achievable answer is simply the original number itself. The algorithm works correctly because the OR of one number is that number.
A third important edge case involves overlapping bit patterns, such as [7, 7, 7]. Multiple numbers may share the same bits, but the maximum achievable XOR still only includes each bit once. The OR operation naturally captures this behavior without double counting bits.
A final important case is when different numbers contribute entirely different bits, such as [1, 2, 4, 8]. In this situation, every bit position can appear in the final answer simultaneously. The algorithm correctly combines all bits into the maximum value 15.