1835. Find XOR Sum of All Pairs Bitwise AND
Problem Description
You are given two arrays arr1
and arr2
that contain only non-negative integers. The problem asks you to find the XOR sum of all possible bitwise AND operations between pairs of elements from the two arrays.
Specifically:
- For every pair
(i, j)
wherei
is an index inarr1
andj
is an index inarr2
, calculatearr1[i] AND arr2[j]
(bitwise AND operation) - Collect all these AND results into a list
- Find the XOR sum of this list (XOR all elements together)
XOR sum means applying the bitwise XOR operation to all elements in sequence. For example:
- XOR sum of
[1,2,3,4]
=1 XOR 2 XOR 3 XOR 4 = 4
- XOR sum of
[3]
=3
The mathematical insight here is that due to the distributive property of AND over XOR in Boolean algebra, the result can be simplified to:
- First, find the XOR sum of all elements in
arr1
(let's call ita
) - Then, find the XOR sum of all elements in
arr2
(let's call itb
) - The final answer is
a AND b
This works because XOR behaves like addition and AND behaves like multiplication in Boolean algebra, allowing us to factor out the expression:
(a₁ AND b₁) XOR (a₁ AND b₂) XOR ... XOR (aₙ AND bₘ) = (a₁ XOR a₂ XOR ... XOR aₙ) AND (b₁ XOR b₂ XOR ... XOR bₘ)
Intuition
Let's think about what happens when we compute all possible AND operations and then XOR them together. If we were to naively compute this, we'd need to calculate n * m
AND operations (where n
and m
are the lengths of the two arrays), store all results, and then XOR them together.
But there's a beautiful mathematical property we can exploit here. In Boolean algebra, XOR and AND operations have a special relationship - they follow distributive laws similar to addition and multiplication in regular algebra.
Consider a simple example with arr1 = [a, b]
and arr2 = [c, d]
. The naive approach would compute:
(a AND c) XOR (a AND d) XOR (b AND c) XOR (b AND d)
Notice how we can group terms:
- Terms with
a
:(a AND c) XOR (a AND d) = a AND (c XOR d)
- Terms with
b
:(b AND c) XOR (b AND d) = b AND (c XOR d)
Now we have: (a AND (c XOR d)) XOR (b AND (c XOR d))
We can factor out (c XOR d)
: (a XOR b) AND (c XOR d)
This pattern generalizes! The key insight is that AND distributes over XOR in a way that allows us to separate the contributions from each array. Instead of computing all pairwise AND operations, we can:
- First compute the XOR of all elements in
arr1
- Then compute the XOR of all elements in
arr2
- Finally, AND these two XOR results together
This reduces our time complexity from O(n * m)
to O(n + m)
, which is a massive improvement. The mathematical elegance here is that the complex interaction of all pairs simplifies to a single AND operation between two XOR sums.
Learn more about Math patterns.
Solution Approach
Based on the mathematical insight that the XOR sum of all AND operations can be simplified to (XOR of arr1) AND (XOR of arr2)
, the implementation becomes remarkably straightforward.
Step 1: Calculate XOR sum of arr1
We iterate through all elements in arr1
and XOR them together. This can be done using Python's reduce
function with the xor
operator:
a = reduce(xor, arr1)
This computes arr1[0] XOR arr1[1] XOR ... XOR arr1[n-1]
Step 2: Calculate XOR sum of arr2
Similarly, we compute the XOR of all elements in arr2
:
b = reduce(xor, arr2)
This computes arr2[0] XOR arr2[1] XOR ... XOR arr2[m-1]
Step 3: Apply bitwise AND Finally, we perform a single AND operation between the two XOR results:
return a & b
The complete solution uses Python's reduce
function from the functools
module along with the xor
function from the operator
module. The reduce
function applies the XOR operation cumulatively to the elements of each array, effectively computing the XOR sum.
Time Complexity: O(n + m)
where n
and m
are the lengths of arr1
and arr2
respectively. We only need to traverse each array once.
Space Complexity: O(1)
as we only use two variables to store the XOR sums, regardless of input size.
This elegant solution transforms what could have been an O(n * m)
problem with potentially massive memory requirements into a linear-time algorithm with constant space usage, all thanks to the distributive property of Boolean operations.
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 arr1 = [1, 6, 3]
and arr2 = [5, 2]
.
Naive Approach (for comparison): First, let's see what we're actually computing by listing all pairwise AND operations:
1 AND 5 = 1
(binary: 001 AND 101 = 001)1 AND 2 = 0
(binary: 001 AND 010 = 000)6 AND 5 = 4
(binary: 110 AND 101 = 100)6 AND 2 = 2
(binary: 110 AND 010 = 010)3 AND 5 = 1
(binary: 011 AND 101 = 001)3 AND 2 = 2
(binary: 011 AND 010 = 010)
XOR sum of all results: 1 XOR 0 XOR 4 XOR 2 XOR 1 XOR 2
1 XOR 0 = 1
1 XOR 4 = 5
5 XOR 2 = 7
7 XOR 1 = 6
6 XOR 2 = 4
Result: 4
Optimized Approach: Now let's use our mathematical insight:
Step 1: Calculate XOR sum of arr1
1 XOR 6 XOR 3
1 XOR 6 = 7
(binary: 001 XOR 110 = 111)7 XOR 3 = 4
(binary: 111 XOR 011 = 100)- XOR sum of arr1 = 4
Step 2: Calculate XOR sum of arr2
5 XOR 2
5 XOR 2 = 7
(binary: 101 XOR 010 = 111)- XOR sum of arr2 = 7
Step 3: Apply bitwise AND
4 AND 7 = 4
(binary: 100 AND 111 = 100)- Final result: 4
Both approaches give us the same answer of 4, confirming our mathematical optimization works! The key difference is that the naive approach required 6 AND operations and 5 XOR operations, while the optimized approach only needed 2 XOR operations and 1 AND operation.
Solution Implementation
1from functools import reduce
2from operator import xor
3from typing import List
4
5class Solution:
6 def getXORSum(self, arr1: List[int], arr2: List[int]) -> int:
7 # Calculate XOR of all elements in arr1
8 # This gives us a single value representing the XOR combination of arr1
9 xor_arr1 = reduce(xor, arr1)
10
11 # Calculate XOR of all elements in arr2
12 # This gives us a single value representing the XOR combination of arr2
13 xor_arr2 = reduce(xor, arr2)
14
15 # The result is the bitwise AND of the two XOR values
16 # This works due to the distributive property:
17 # (a1 XOR a2 XOR ... an) AND (b1 XOR b2 XOR ... bm) =
18 # XOR of all (ai AND bj) for all i, j pairs
19 return xor_arr1 & xor_arr2
20
1class Solution {
2 public int getXORSum(int[] arr1, int[] arr2) {
3 // Calculate XOR of all elements in arr1
4 int xorSum1 = 0;
5 for (int element : arr1) {
6 xorSum1 ^= element;
7 }
8
9 // Calculate XOR of all elements in arr2
10 int xorSum2 = 0;
11 for (int element : arr2) {
12 xorSum2 ^= element;
13 }
14
15 // The XOR sum of all pairwise ANDs equals (XOR of arr1) AND (XOR of arr2)
16 // This optimization reduces time complexity from O(n*m) to O(n+m)
17 return xorSum1 & xorSum2;
18 }
19}
20
1class Solution {
2public:
3 int getXORSum(vector<int>& arr1, vector<int>& arr2) {
4 // Calculate XOR of all elements in arr1
5 // accumulate applies bit_xor operation sequentially to all elements
6 // starting with initial value 0
7 int xor_sum_arr1 = accumulate(arr1.begin(), arr1.end(), 0, bit_xor<int>());
8
9 // Calculate XOR of all elements in arr2
10 // Same operation as above for the second array
11 int xor_sum_arr2 = accumulate(arr2.begin(), arr2.end(), 0, bit_xor<int>());
12
13 // Return the bitwise AND of the two XOR sums
14 // This gives us the XOR sum of all possible (arr1[i] & arr2[j]) pairs
15 // due to the distributive property: (a XOR b) AND (c XOR d) = XOR of all (a[i] AND c[j])
16 return xor_sum_arr1 & xor_sum_arr2;
17 }
18};
19
1/**
2 * Calculates the XOR sum of two arrays using bitwise operations.
3 * First computes the XOR of all elements in each array,
4 * then returns the bitwise AND of the two results.
5 *
6 * @param arr1 - The first array of numbers
7 * @param arr2 - The second array of numbers
8 * @returns The bitwise AND of the XOR sums of both arrays
9 */
10function getXORSum(arr1: number[], arr2: number[]): number {
11 // Calculate XOR of all elements in the first array
12 const xorSumArr1: number = arr1.reduce((accumulator: number, currentValue: number) => accumulator ^ currentValue);
13
14 // Calculate XOR of all elements in the second array
15 const xorSumArr2: number = arr2.reduce((accumulator: number, currentValue: number) => accumulator ^ currentValue);
16
17 // Return the bitwise AND of both XOR sums
18 return xorSumArr1 & xorSumArr2;
19}
20
Time and Space Complexity
The time complexity is O(n + m)
, where n
is the length of arr1
and m
is the length of arr2
. This is because:
- The first
reduce(xor, arr1)
operation iterates through alln
elements ofarr1
, performing XOR operations inO(n)
time - The second
reduce(xor, arr2)
operation iterates through allm
elements ofarr2
, performing XOR operations inO(m)
time - The final AND operation between
a
andb
takesO(1)
time - Total:
O(n) + O(m) + O(1) = O(n + m)
The space complexity is O(1)
. The algorithm only uses two additional variables a
and b
to store the XOR results, regardless of the input size. The reduce
function operates iteratively without creating additional data structures proportional to the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting the Brute Force Approach
Many developers initially try to solve this problem by computing all possible AND operations and then finding their XOR sum:
# Inefficient approach - DON'T DO THIS
def getXORSum_bruteforce(arr1, arr2):
result = 0
for num1 in arr1:
for num2 in arr2:
result ^= (num1 & num2)
return result
Why it's problematic:
- Time complexity becomes O(n × m), which can be extremely slow for large arrays
- With constraints like arrays having up to 10^5 elements, this leads to 10^10 operations
- May cause Time Limit Exceeded (TLE) errors on platforms like LeetCode
Solution: Use the mathematical property that allows simplification to (XOR of arr1) & (XOR of arr2)
, reducing complexity to O(n + m).
2. Misunderstanding the Order of Operations
Some might incorrectly compute AND first, then XOR:
# WRONG - This doesn't give the correct result
def getXORSum_wrong(arr1, arr2):
and_arr1 = reduce(lambda x, y: x & y, arr1) # AND all elements
and_arr2 = reduce(lambda x, y: x & y, arr2) # AND all elements
return and_arr1 ^ and_arr2 # XOR the results
Why it's wrong: The problem asks for XOR of all AND pairs, not AND of all elements then XOR. The correct approach requires XOR-ing within each array first, then AND-ing the results.
Solution: Remember the correct formula: (a1 XOR a2 XOR ... an) AND (b1 XOR b2 XOR ... bm)
3. Edge Case Handling
Not considering edge cases with single-element arrays or when XOR results in 0:
# Potential issue with empty array handling
def getXORSum_edge_case(arr1, arr2):
if not arr1 or not arr2: # This check might not be needed
return 0
return reduce(xor, arr1) & reduce(xor, arr2)
Why it matters: While the problem states arrays contain non-negative integers (implying non-empty), reduce
without an initial value will fail on empty sequences.
Solution: Either validate input constraints or provide an initial value to reduce:
xor_arr1 = reduce(xor, arr1, 0) # 0 is the identity element for XOR xor_arr2 = reduce(xor, arr2, 0)
4. Not Recognizing the Mathematical Pattern
Trying to memorize the solution without understanding why it works can lead to errors in similar problems. The key insight is the distributive property: a & (b XOR c) = (a & b) XOR (a & c)
.
Solution: Understand that this property allows us to factor out the expression, similar to how multiplication distributes over addition in regular algebra. This pattern appears in other bit manipulation problems as well.
How many ways can you arrange the three letters A, B and C?
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!