Imagine you're given an array of 100,000 elements, and you need to answer 100,000 queries of the form: "what's the XOR of elements from index L to R?"
The brute force approach โ looping from L to R for each query โ would take 10 billion operations. That's O(n ร q) and it will NOT pass.
Enter Prefix XOR. The idea is dead simple: precompute aprefix array where prefix[i] stores the XOR of all elements from index 0 to i-1. Then any range query becomes a single XOR operation:
Key Insight
๐ The Magic Formula: XOR[l..r] = prefix[r+1] ^ prefix[l]
This is the prefix sum of the bit world. Build once in O(n), answer every query in O(1). No loops, no brute force, no drama.
And this isn't just for range queries. Prefix XOR unlocks solutions for counting triplets, XOR of all pairings, subset XOR totals, and the XOR beauty of an array. Let's dive in! ๐
1. Prefix XOR Array โ The Foundation
Let's really understand what a prefix XOR array is and how to use it.
Building the Prefix XOR Array
Given an array arr, we buildprefix where:
Note
๐ Real-World Analogy: Imagine you have a long receipt tape showing every purchase you've ever made. To find the total spent between day L and day R, you could add up each day individually โ but that's slow. Instead, you keep a running total (prefix sum) after each day. The amount spent between L and R = total up to R minus total up to L-1. Prefix XOR is EXACTLY this โ but with XOR instead of addition. And since XOR is its own inverse, subtraction IS XOR!
prefix[0] = 0 // XOR of zero elements = 0 (identity)
prefix[1] = arr[0]
prefix[2] = arr[0] ^ arr[1]
prefix[3] = arr[0] ^ arr[1] ^ arr[2]
...
prefix[i] = XOR of arr[0] through arr[i-1]
// In code:
function buildPrefix(arr) {
const prefix = new Array(arr.length + 1);
prefix[0] = 0;
for (let i = 0; i < arr.length; i++) {
prefix[i + 1] = prefix[i] ^ arr[i];
}
return prefix;
}
Each step: prefix[i+1] = prefix[i] ^ arr[i]. It's that simple. We're literally just accumulating the XOR as we walk through the array.
The Range Query Formula
Here's where the MAGIC happens. To get XOR of arr[l..r]:
Tip
๐ก XOR[l..r] = prefix[r+1] ^ prefix[l]
Why does this work? prefix[r+1] = XOR of arr[0..r] prefix[l] = XOR of arr[0..l-1]
XORing them: (arr[0] ^ ... ^ arr[r]) ^ (arr[0] ^ ... ^ arr[l-1]) The first l elements appear twice โ they cancel! Only arr[l..r] remains. ๐ฏ
Key Insight
๐ Key Insight: This works because XOR is its own inverse.prefix[r+1] ^ prefix[l] cancels the first l elements, leaving exactly the XOR of arr[l..r]. If you understand this, you understand every prefix XOR pattern that follows!
2. XOR Queries of a Subarray
Problem: Given an array arr and a list of queries [l, r], return the XOR of each subarrayarr[l..r].
๐ Real-Time Thinking โ What Do I Know?
"I need to answer many range XOR queries. Brute force = O(n) per query, O(n ร q) total โ too slow. But if I precompute a prefix XOR array where prefix[i] = XOR of arr[0..i-1], then XOR of arr[l..r] = prefix[r+1] ^ prefix[l]. Why? prefix[r+1] has everything from 0 to r. prefix[l] has everything from 0 to l-1. XOR them: elements 0..l-1 appear TWICE and cancel (x^x=0). Only arr[l..r] remains. O(n) preprocessing, O(1) per query!"
๐ค Let's Think About It
If you've never seen prefix XOR, you'd probably write a loop for each query:
Brute Force Approach
function xorQueriesBrute(arr, queries) {
const result = [];
for (const [l, r] of queries) {
let xor = 0;
for (let i = l; i <= r; i++) xor ^= arr[i];
result.push(xor);
}
return result;
// Time: O(n ร q) โ might be up to 10^10 ops!
}
โ This gives the right answer, but it's SLOW. Forn = 10โต and q = 10โต, this is 10 billion XOR operations. Your interviewer will NOT be impressed. ๐
๐ Edge Cases to Consider
l = r (single element query): prefix[l+1] ^ prefix[l] = arr[l] ^ 0 = arr[l]. Correct!
l = 0 (query from start): prefix[r+1] ^ prefix[0] = prefix[r+1] ^ 0 = prefix[r+1]. Correct!
l > r: Invalid query. Problem guarantees l โค r, but if not, the formula would give unexpected results.
Empty array: prefix = [0]. Any query would be out of bounds โ no valid queries for empty array.
Large n (10โต): Prefix array of size n+1. O(n) memory. Still fine.
โจ The Prefix XOR Optimal Solution
Build prefix XOR once, then answer every query in O(1):
function xorQueries(arr, queries) {
// Step 1: Build prefix XOR โ O(n)
const prefix = new Array(arr.length + 1);
prefix[0] = 0;
for (let i = 0; i < arr.length; i++) {
prefix[i + 1] = prefix[i] ^ arr[i];
}
// Step 2: Answer each query โ O(1) each
return queries.map(([l, r]) => prefix[r + 1] ^ prefix[l]);
// Total: O(n + q) โ linear in both!
}
๐ Dry Run
Let's trace through arr = [2, 3, 1, 4] with queries:
Query [l, r]
Formula
Calculation
Result
Verify
[0, 2]
prefix[3] ^ prefix[0]
0 ^ 0
0
2^3^1 = 0 โ
[1, 3]
prefix[4] ^ prefix[1]
4 ^ 2
6
3^1^4 = 6 โ
[0, 3]
prefix[4] ^ prefix[0]
4 ^ 0
4
2^3^1^4 = 4 โ
[2, 2]
prefix[3] ^ prefix[2]
0 ^ 1
1
1 = 1 โ
Tip
โก Interview Tip: When you hear "multiple range XOR queries" in an interview, your brain should immediately shout PREFIX XOR. Say it: "We can precompute a prefix XOR array in O(n), then answer each query in O(1) using the formula prefix[r+1] ^ prefix[l]." This shows you understand offline query processing.
PROBLEMXOR Queries of a Subarray
Solve it yourself! Given an array and a list of queries, return the XOR of each subarray efficiently.
Loading playground...
3. Count Triplets โ Equal XOR Subarrays
Problem: Given an array, count the number of triplets(i, j, k) where i < j <= ksuch that XOR[i..j-1] == XOR[j..k].
Note
๐ฏ Real-World Analogy: Imagine you have a row of dominoes numbered 0..n. You're looking for pairs of dominoes with the SAME number on them. Any domino BETWEEN them can be the 'j' divider โ it doesn't matter which one! Because XOR[i..j-1] = XOR[j..k] simplifies to prefix[i] == prefix[k+1]. The j in between is just window dressing โ any j works. So count all prefix value matches!
๐ค The Intuition
This looks complex. Three indices, two subarrays, we need their XOR to be equal. Let's break it down using โ you guessed it โ prefix XOR!
Let's express both sides using our prefix array:
// XOR[i..j-1] = prefix[j] ^ prefix[i]
// XOR[j..k] = prefix[k+1] ^ prefix[j]
// They're equal:
prefix[j] ^ prefix[i] = prefix[k+1] ^ prefix[j]
// XOR both sides with prefix[j]:
prefix[i] = prefix[k+1]
// ๐ก THE INSIGHT: prefix[i] MUST EQUAL prefix[k+1]!
So the condition XOR[i..j-1] == XOR[j..k] simplifies toprefix[i] == prefix[k+1]. The value of j doesn't matter for the equality โ any j between i and k works!
Counting the Triplets
For each pair (i, k) whereprefix[i] == prefix[k+1]:
The number of valid j positions = k - i (j can be i+1 through k)
But wait โ the problem says i < j <= k, so j โ [i+1, k]
That gives k - i choices for j
Algorithm
function countTriplets(arr) {
const n = arr.length;
const prefix = new Array(n + 1);
prefix[0] = 0;
for (let i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] ^ arr[i];
}
let count = 0;
// For every pair (i, k) where i < k
for (let i = 0; i < n; i++) {
for (let k = i + 1; k < n; k++) {
if (prefix[i] === prefix[k + 1]) {
count += k - i; // all j between i and k work
}
}
}
return count;
}
// Time: O(nยฒ), Space: O(n)
// Can be optimized to O(n) using a hash map!
Optimized O(n) Solution
We can do this in a single pass. For each position k, we track how many times we've seen each prefix value before. When we encounterprefix[k+1] again, we add the sum of indices to our count:
function countTripletsOptimized(arr) {
const n = arr.length;
let count = 0;
let xor = 0;
// Map: prefixValue -> { countSoFar, sumOfIndices }
const map = new Map();
map.set(0, { count: 1, sum: 0 });
for (let k = 0; k < n; k++) {
xor ^= arr[k]; // this is prefix[k+1]
const entry = map.get(xor);
if (entry) {
// Each previous position i where prefix[i] == xor
// contributes (k - i) = k * count - sumOfIndices
count += k * entry.count - entry.sum;
entry.count++;
entry.sum += k + 1; // or just k depending on i indexing
} else {
map.set(xor, { count: 1, sum: k + 1 });
}
}
return count;
}
Key Insight
๐ The Key Insight: The triplets problem LOOKS like it needs three nested loops (O(nยณ)), but with prefix XOR we reduce it to O(nยฒ), and with a hash map we get O(n). The essence? prefix[i] == prefix[k] means any j in between works.
PROBLEMCount Triplets That Can Form Two Arrays of Equal XOR
Count the number of triplets (i, j, k) where XOR[i..j-1] == XOR[j..k].
Loading playground...
4. XOR of All Pairings
Problem: Given an array, compute the XOR of allnums[i] << 16 | nums[j] pairings... actually, wait. Let me rephrase.
The problem: Given two arrays nums1 andnums2, form a new array where i-thelement of nums1 is paired with every element ofnums2. Compute the XOR of all paired values.
๐ค The Decision: Contribute or Cancel?
For each element, I need to know: "Does this element appear an even or odd number of times in all pairings?" Each element in nums1 is paired with EVERY element of nums2 โ that's m times. If m is odd, the element contributes to the final XOR (odd occurrences don't cancel). If m is even, it cancels out. Same logic for nums2 elements: each appears n times. So I only need to XOR nums1 if m is odd, and XOR nums2 if n is odd!
๐ค Understanding the Pattern
Let's start simpler. What if we have ONE array [a, b, c] and we XOR all possible pairs (a,b), (a,c), (b,c)?
// Array: [a, b, c]
// All pairs: (a,b), (a,c), (b,c)
// XOR everything:
(a^b) ^ (a^c) ^ (b^c)
= a^b^a^c^b^c
= (a^a) ^ (b^b) ^ (c^c)
= 0 ^ 0 ^ 0
= 0
// Each element appears (n-1) = 2 times โ EVEN โ all cancel!
Now what if [a, b, c, d]?
// Array: [a, b, c, d]
// All pairs: (a,b), (a,c), (a,d), (b,c), (b,d), (c,d)
// a appears 3 times, b appears 3 times, etc.
// Each element appears (n-1) = 3 times โ ODD โ contributes
// Result = a ^ b ^ c ^ d
The General Rule
Tip
๐ก Each element appears in (n-1) pairs. If (n-1) is even (n is odd) โ each element appears an even number of times โ ALL cancel โ result = 0. If (n-1) is odd (n is even) โ each element appears an odd number of times โ each contributes โ result = XOR of all elements.
The Actual Problem: Two Arrays
Now for the real problem: we have nums1 (length n) andnums2 (length m). Every element of nums1 is paired with every element of nums2.
How many times does a particular element appear?
Each element of nums1 appears in m pairs
Each element of nums2 appears in n pairs
function xorAllPairings(nums1, nums2) {
const n = nums1.length;
const m = nums2.length;
let result = 0;
// nums1 elements appear m times each
if (m % 2 === 1) { // m is odd โ each nums1[i] contributes
for (const num of nums1) result ^= num;
}
// nums2 elements appear n times each
if (n % 2 === 1) { // n is odd โ each nums2[i] contributes
for (const num of nums2) result ^= num;
}
return result;
}
// Example:
// nums1 = [2, 4], nums2 = [1, 3, 5]
// n=2 (even โ nums2 cancel), m=3 (odd โ nums1 contribute)
// Result = 2 ^ 4 = 6
Warning
โ ๏ธ Common Mistake: The problem has TWO arrays. Don't apply the single-array rule directly. Think about how many times each element participates in a pair. An element in nums1 is paired with EVERY element of nums2 โ that's m pairs. Similarly, an element in nums2 is paired n times. Only elements that appear an ODD number of times contribute.
PROBLEMBitwise XOR of All Pairings
Given two arrays nums1 and nums2, pair every element of nums1 with every element of nums2. Compute XOR of all paired values.
Loading playground...
5. Find Original Array of Prefix XOR
Problem: Given an array pref wherepref[i] is the XOR of arr[0..i](note: this is a different definition than our prefix array), reconstruct the original arr.
Note
๐ Real-World Analogy: This is like being given the running total after each step, and needing to figure out each individual step amount. For prefix SUM, you'd compute arr[i] = prefix[i] - prefix[i-1]. For prefix XOR, since XOR is its own inverse, arr[i] = prefix[i] ^ prefix[i-1]. It's the EXACT same idea โ just with XOR instead of subtraction!
๐ค The Reverse Operation
This is the inverse of building prefix XOR. If you know how to build it, you know how to reverse it. Let's think:
๐ The Inverse Insight: XOR is its own inverse. Ifpref[i] = pref[i-1] ^ arr[i], thenarr[i] = pref[i] ^ pref[i-1]. This is exactly like saying: if sum[i] = sum[i-1] + arr[i], thenarr[i] = sum[i] - sum[i-1]. Prefix XOR and prefix sum follow the same logic โ just with XOR instead of addition!
PROBLEMFind the Original Array of Prefix XOR
You are given an array pref of size n. pref[i] is the XOR of arr[0..i]. Reconstruct arr.
Loading playground...
6. Sum of All Subset XOR Totals
Problem: Given an array, find the sum of XOR ofall subsets. For each subset, compute the XOR of its elements, then sum all those XOR values.
๐ Real-Time Thinking โ What Do I Know?
"I need the SUM of XOR of ALL subsets. There are 2โฟ subsets โ brute-forcing is impossible for n > 20. But XOR is bit-independent! For each bit position, I ask: 'How many subsets have this bit set in their XOR?' If cnt elements have this bit set, then exactly half of all 2โฟ subsets (2โฟโปยน) will have this bit set in their XOR โ because the number of ways to pick an odd number from cnt elements is 2^(cnt-1), times 2^(n-cnt) for the rest. So the contribution of this bit is 2โฟโปยน ร 2^b. The total = (OR of all elements) ร 2โฟโปยน!"
๐ค Let's Think About This
For an array of size n, there are 2โฟ subsets. If n is 12 or less, we could brute force. But for n up to 20 or 30, we need a smarter formula.
Key insight: Instead of iterating over subsets, we can think about each bit position independently and count how many subsets have that bit set in their XOR.
Bit Contribution Approach
For bit position b, let's say there arecnt elements with that bit set to 1.
For a subset's XOR to have bit b = 1, the subset must contain an oddnumber of elements with bit b set.
// Number of ways to pick odd number of set-bit elements:
// = (2^(cnt-1)) [combinatorics: half of all subsets have odd count]
// Number of ways to pick any subset of non-set-bit elements:
// = 2^(n-cnt)
// Total subsets with bit b set in XOR:
// = 2^(cnt-1) * 2^(n-cnt) = 2^(n-1)
// So each bit contributes: 2^b * 2^(n-1) = 2^(n-1) * 2^b
// But only if cnt > 0! (If cnt=0, no subset has this bit set)
The Formula
Tip
๐ก Sum of all subset XOR totals = (OR of all elements) * 2^(n-1)
Wait, what? Let me explain. If we compute the OR of all elements, that tells us which bit positions have at least one element with that bit set. Then we multiply by 2^(n-1) because each such bit contributes2^b * 2^(n-1) to the total sum.
function subsetXORSum(nums) {
let orAll = 0;
for (const num of nums) orAll |= num;
return orAll * (1 << (nums.length - 1));
}
// Example: nums = [1, 3]
// OR = 1 | 3 = 3 (binary 11)
// n = 2, 2^(n-1) = 2^1 = 2
// Result = 3 * 2 = 6
// Let's verify with all subsets:
// {} โ {} โ XOR = 0
// {1} โ {1} โ XOR = 1
// {3} โ {3} โ XOR = 3
// {[1, 3]} โ {[1, 3]} โ XOR = 2
// Sum = 0 + 1 + 3 + 2 = 6 โ
Dry Run โ Why Each Bit Contributes 2^(n-1)
Bit position
Elements with bit = 1
cnt
Subsets with odd cnt โ bit set in XOR
Contribution
0
[1] (1 has bit 0 = 1)
1
2^(1-1) * 2^(2-1) = 2 subsets
2 ร 1 = 2
1
[3] (3 has bit 1 = 1)
1
2^(1-1) * 2^(2-1) = 2 subsets
2 ร 2 = 4
Total
6
Key Insight
๐ The Key Insight: For each bit position that appears in at least one element, exactly half of all subsets (2โฟ / 2 = 2โฟโปยน) will have that bit set in their XOR. This is because choosing an odd number of set-bit elements from a pool of cnt โฅ 1 is always 2^(cnt-1) choices, and the remaining elements (those without the bit) contribute 2^(n-cnt) choices. Multiply them: 2^(cnt-1) ร 2^(n-cnt) = 2^(n-1).
Warning
โ ๏ธ Edge Case: If the array has only one element (n=1), then 2^(n-1) = 2^0 = 1. The formula becomes OR * 1 = OR. Let's check: subsets of [a] are {} and {a}. XORs are 0 and a. Sum = a. OR of [a] = a. So a * 1 = a โ . Works for edge cases too!
PROBLEMSum of All Subset XOR Totals
Given an array, find the sum of XOR totals of all subsets. Solve in O(n) using the bit contribution formula.
Loading playground...
7. Find XOR Beauty of Array
Problem: The XOR beauty of an array is defined as the XOR of(nums[i] | nums[j]) & nums[k] for all triplets(i, j, k) where0 <= i < j < k < n.
Note
๐ Real-World Analogy: Imagine a gemstone with 32 facets (bits). The beauty of the stone is the XOR of (OR of two facets) AND a third facet, for every possible trio. This sounds incredibly complex (triple loops!), but XOR's cancellation property simplifies it massively. A bit contributes to the final beauty if at least TWO elements have that bit set. Two is the magic number โ the combinatorics of triplets cause counts of 0 or 1 to cancel out!
๐ค Breaking Down the Problem
This looks INTIMIDATING. Three nested loops? That's O(nยณ). But XOR gives us a massive shortcut. Let's think bit by bit.
For a fixed bit position b:
nums[i] | nums[j] has bit b = 1 if at least one of nums[i], nums[j] has bit b = 1
Then (OR) & nums[k] has bit b = 1 if AND with nums[k] also has bit b = 1
So the whole expression has bit b = 1 iff: at least one of i,j has bit b AND k has bit b
Counting Contributions
Let cnt = number of elements with bit b = 1.
For a fixed k with bit b = 1:
We need pairs (i, j) where i < j < k and at least one of them has bit b = 1
Total pairs before k: C(k, 2)
Pairs where neither has bit b: C(k - onesBeforeK, 2) where onesBeforeK is count of elements before k with bit b set
Valid pairs = total pairs - bad pairs
The total count per bit needs to be odd for the bit to contribute (since we're XORing everything). After analysis, the formula simplifies beautifully:
Tip
๐ก XOR Beauty Formula: For each bit position, if the count of elements with that bit set isat least 2, that bit contributes 2^b to the final answer.
function findXORBeauty(nums) {
const n = nums.length;
let result = 0;
for (let bit = 0; bit < 32; bit++) {
let count = 0;
for (const num of nums) {
if ((num >> bit) & 1) count++;
}
if (count >= 2) {
result |= (1 << bit);
}
}
return result;
}
// Example: nums = [1, 3, 5]
// Bit 0: count = 2 (1, 3, 5 have bit 0 set: 1 has it, 3 has it, 5 has it) โ count=3? No...
// 1 = 001 โ bit 0 = 1, bit 1 = 0, bit 2 = 0
// 3 = 011 โ bit 0 = 1, bit 1 = 1, bit 2 = 0
// 5 = 101 โ bit 0 = 1, bit 1 = 0, bit 2 = 1
// Bit 0: count = 3 (all three have it) โ โฅ 2 โ contributes
// Bit 1: count = 1 (only 3 has it) โ < 2 โ doesn't contribute
// Bit 2: count = 1 (only 5 has it) โ < 2 โ doesn't contribute
// Result = 2^0 = 1
Why count โฅ 2?
Intuitively: if only one element has a particular bit set, then in any triplet (i, j, k), the "OR" step can only use that single element for that bit, and the "AND" step needs k to also have it. The combinatorics work out such that the total number of triplets with that bit set is always even โ meaning it cancels out in the final XOR.
But if at least two elements have the bit set, the count flips to odd, and the bit contributes.
Key Insight
๐ The XOR Beauty Insight: This problem looks terrifying at first (triple nested loops over triplets!), but XOR's cancellation property and bit-independence let us solve it in O(n ร 32) = O(n). The conditioncount โฅ 2 per bit is all we need. Beautiful, right? ๐
PROBLEMFind XOR Beauty of Array
Given an array, compute the XOR beauty defined as XOR of (nums[i] | nums[j]) & nums[k] over all triplets (i,j,k).
Loading playground...
๐ฏ Interview Cheat Sheet
Key Insight
Q1: When should I use prefix XOR vs prefix sum? Prefix XOR for XOR range queries, prefix sum for arithmetic range queries. Both follow the same pattern: range = prefix[r+1] - prefix[l]for sum, range = prefix[r+1] ^ prefix[l] for XOR.
Key Insight
Q2: How do I prove that XOR[l..r] = prefix[r+1] ^ prefix[l]? prefix[r+1] = XOR of arr[0..r]. prefix[l] = XOR of arr[0..l-1]. XOR them: the first l elements appear twice โ cancel. Only arr[l..r] remains.
Key Insight
Q3: What's the pattern for "every element paired with every other element"? Each element of array A appears (length of B) times in the pairings. Only elements paired an ODD number of times contribute to the final XOR.
Key Insight
Q4: How do I count triplets with equal XOR subarrays? XOR[i..j-1] == XOR[j..k] simplifies toprefix[i] == prefix[k]. Any j in between works. Count = sum of (k - i) for each pair (i, k) with equal prefix values.
Key Insight
Q5: Why does each bit contribute 2^(n-1) to subset XOR sum? For a bit that appears in at least one element, exactly half of all 2โฟ subsets have an odd count of elements with that bit set. So each such bit contributes 2^b ร 2^(n-1) to the total sum. The formula becomes OR ร 2^(n-1).
Key Insight
Q6: What's the pattern for reconstructing the original array from prefix XOR? If pref[i] = arr[0] ^ ... ^ arr[i], thenarr[i] = pref[i] ^ pref[i-1]. This is the inverse of building prefix XOR, using XOR's self-inverse property.
๐ Key Takeaways
Prefix XOR is the prefix sum of the bit world โ build once in O(n), answer range queries in O(1)
The formula prefix[r+1] ^ prefix[l] cancels the first l elements, leaving XOR of arr[l..r]
Equal XOR subarrays โ equal prefix values โ for triplet problems, prefix[i] == prefix[k] is the condition to check
Pairing patterns โ each element participates in (n-1) or (m) pairs; odd count โ contributes, even โ cancels
Subset XOR sum โ each bit present in any element contributes 2^(n-1) ร 2^b; formula = OR of all ร 2^(n-1)
XOR beauty โ for each bit, if count โฅ 2, that bit contributes. No triple loop needed!
Think bit-by-bit โ most XOR problems decompose into independent per-bit analysis
Time: O(n), Space: O(n) for prefix, can often be reduced to O(1) with running XOR
Practice! โ These patterns click with repetition. Hit the Playground below each problem ๐ฅ
Note
๐ฎ What's Next? You've mastered XOR subarray patterns! Now head over toBit Countingto learn about popcount, Hamming distance, and more bit manipulation tricks!