2317. Maximum XOR After Operations
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:
- Choose any non-negative integer
x
- Choose any index
i
in the array - Update
nums[i]
to becomenums[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
.
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
, then0 AND anything = 0
, so it stays0
- If the bit is
1
, then1 AND (1 XOR x_bit)
depends on whatx_bit
is:- If
x_bit = 0
: we get1 AND (1 XOR 0) = 1 AND 1 = 1
(bit stays1
) - If
x_bit = 1
: we get1 AND (1 XOR 1) = 1 AND 0 = 0
(bit becomes0
)
- If
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 have1
in that position - If that bit is
1
in an even number of elements, the XOR will have0
in that position
Since we can turn 1
s into 0
s 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 the1
while others have0
- 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:
-
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. -
or_
operator: This is the bitwise OR operator from Python'soperator
module. It performs bitwise OR between two numbers. -
How it works:
reduce(or_, nums)
takes the first two elements ofnums
, 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 is7
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 EvaluatorExample 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 choosingx = 0
- Or turn it into
000
by choosingx = 1
(since001 AND (001 XOR 001) = 001 AND 000 = 000
)
For nums[1] = 2 = 010
:
- We can keep it as
010
by choosingx = 0
- Or turn it into
000
by choosingx = 2
(since010 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 choosingx = 1
(since011 AND (011 XOR 001) = 011 AND 010 = 010
) - Or turn it into
001
by choosingx = 2
(since011 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
andnums[2]=3
. We can make exactly one element have this bit. - Bit position 1: Present in
nums[1]=2
andnums[2]=3
. We can make exactly one element have this bit.
One optimal configuration:
- Transform
nums[0] = 1
to000
(using operation withx=1
) - Keep
nums[1] = 2
as010
- Transform
nums[2] = 3
to001
(using operation withx=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
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!