1738. Find Kth Largest XOR Coordinate Value
Problem Description
You have a 2D matrix of size m x n
containing non-negative integers, and you need to find the k-th largest value among all coordinate values in the matrix.
For any coordinate (a, b)
in the matrix, its value is calculated as the XOR of all elements in the rectangular region from the top-left corner (0, 0)
to (a, b)
inclusive. In other words, the value at coordinate (a, b)
is:
matrix[0][0] XOR matrix[0][1] XOR ... XOR matrix[0][b] XOR matrix[1][0] XOR ... XOR matrix[a][b]
This includes all elements where row index i
satisfies 0 <= i <= a
and column index j
satisfies 0 <= j <= b
.
Your task is to:
- Calculate the XOR value for every coordinate in the matrix
- Find the k-th largest value among all these calculated XOR values (where k is 1-indexed, meaning k=1 refers to the largest value)
For example, if you have a 2x2 matrix, you would calculate 4 different XOR values:
- Value at
(0,0)
=matrix[0][0]
- Value at
(0,1)
=matrix[0][0] XOR matrix[0][1]
- Value at
(1,0)
=matrix[0][0] XOR matrix[1][0]
- Value at
(1,1)
=matrix[0][0] XOR matrix[0][1] XOR matrix[1][0] XOR matrix[1][1]
Then return the k-th largest among these values.
Intuition
The naive approach would be to calculate the XOR value for each coordinate by iterating through all elements in its rectangle, but this would be inefficient with time complexity of O(m²n²)
.
The key insight is recognizing that we can use prefix XOR similar to how we use prefix sums. Just like prefix sums help us quickly calculate the sum of any subarray, prefix XOR can help us calculate the XOR of any subrectangle.
Let's think about how XOR operations behave. A crucial property of XOR is that a XOR a = 0
and a XOR 0 = a
. This means if we XOR the same value twice, it cancels out.
Consider calculating the XOR value at coordinate (i, j)
. If we already know:
- The XOR of all elements from
(0,0)
to(i-1, j)
- The XOR of all elements from
(0,0)
to(i, j-1)
- The XOR of all elements from
(0,0)
to(i-1, j-1)
We can use these to compute the XOR at (i, j)
. When we XOR the first two values, we get all elements in the rectangles, but the overlap region from (0,0)
to (i-1, j-1)
gets XORed twice and cancels out. So we need to XOR it back once more, along with the current element matrix[i][j]
.
This gives us the formula:
s[i+1][j+1] = s[i+1][j] XOR s[i][j+1] XOR s[i][j] XOR matrix[i][j]
By building up this 2D prefix XOR array, we can calculate all coordinate values in O(mn)
time. Once we have all values, finding the k-th largest is a standard problem that can be solved by sorting them in O(mn log(mn))
or using a more efficient quick selection algorithm in O(mn)
average time.
Learn more about Divide and Conquer, Prefix Sum, Sorting and Heap (Priority Queue) patterns.
Solution Approach
We implement the solution using a 2D prefix XOR array combined with sorting to find the k-th largest value.
Step 1: Initialize the Prefix XOR Array
We create a 2D array s
of size (m+1) x (n+1)
initialized with zeros. The extra row and column act as padding to handle boundary cases elegantly, avoiding the need for special checks when i=0
or j=0
.
Step 2: Build the Prefix XOR Array
We iterate through each cell of the original matrix and calculate the prefix XOR value using the formula:
s[i+1][j+1] = s[i+1][j] XOR s[i][j+1] XOR s[i][j] XOR matrix[i][j]
Let's understand each component:
s[i+1][j]
: XOR of all elements from(0,0)
to(i, j-1)
s[i][j+1]
: XOR of all elements from(0,0)
to(i-1, j)
s[i][j]
: XOR of all elements from(0,0)
to(i-1, j-1)
(added back because it was XORed twice)matrix[i][j]
: The current element
As we calculate each prefix XOR value, we append it to an ans
list that will store all coordinate values.
Step 3: Find the k-th Largest Value
After calculating all m*n
coordinate values, we need to find the k-th largest. The solution uses Python's nlargest(k, ans)
function from the heapq
module, which efficiently finds the k largest elements. The function returns these k elements in descending order, so nlargest(k, ans)[-1]
gives us the k-th largest value.
Time Complexity: O(mn + mn*log(k))
where the first term is for building the prefix XOR array and the second term is for finding the k-th largest element using a heap.
Space Complexity: O(mn)
for storing the prefix XOR array and the list of all coordinate values.
The beauty of this approach is that it transforms a potentially expensive repeated XOR calculation problem into an efficient dynamic programming solution using the prefix XOR technique.
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 small example with a 2×3 matrix and find the k=4th largest value.
Given matrix:
[5, 2, 1] [1, 6, 3]
Step 1: Initialize prefix XOR array s
(3×4 with padding):
[0, 0, 0, 0] [0, ?, ?, ?] [0, ?, ?, ?]
Step 2: Build the prefix XOR array using the formula:
s[i+1][j+1] = s[i+1][j] XOR s[i][j+1] XOR s[i][j] XOR matrix[i][j]
For position (0,0): matrix value = 5
s[1][1] = s[1][0] XOR s[0][1] XOR s[0][0] XOR 5
s[1][1] = 0 XOR 0 XOR 0 XOR 5 = 5
- Add 5 to our answer list:
ans = [5]
For position (0,1): matrix value = 2
s[1][2] = s[1][1] XOR s[0][2] XOR s[0][1] XOR 2
s[1][2] = 5 XOR 0 XOR 0 XOR 2 = 7
- Add 7 to answer list:
ans = [5, 7]
For position (0,2): matrix value = 1
s[1][3] = s[1][2] XOR s[0][3] XOR s[0][2] XOR 1
s[1][3] = 7 XOR 0 XOR 0 XOR 1 = 6
- Add 6 to answer list:
ans = [5, 7, 6]
For position (1,0): matrix value = 1
s[2][1] = s[2][0] XOR s[1][1] XOR s[1][0] XOR 1
s[2][1] = 0 XOR 5 XOR 0 XOR 1 = 4
- Add 4 to answer list:
ans = [5, 7, 6, 4]
For position (1,1): matrix value = 6
s[2][2] = s[2][1] XOR s[1][2] XOR s[1][1] XOR 6
s[2][2] = 4 XOR 7 XOR 5 XOR 6 = 0
- Add 0 to answer list:
ans = [5, 7, 6, 4, 0]
For position (1,2): matrix value = 3
s[2][3] = s[2][2] XOR s[1][3] XOR s[1][2] XOR 3
s[2][3] = 0 XOR 6 XOR 7 XOR 3 = 2
- Add 2 to answer list:
ans = [5, 7, 6, 4, 0, 2]
Final prefix XOR array:
[0, 0, 0, 0] [0, 5, 7, 6] [0, 4, 0, 2]
Step 3: Find the 4th largest value
All coordinate values: [5, 7, 6, 4, 0, 2]
Sorted in descending order: [7, 6, 5, 4, 2, 0]
The 4th largest value is 4.
Verification of some values:
- Value at (0,1) = 5 XOR 2 = 7 ✓
- Value at (1,1) = 5 XOR 2 XOR 1 XOR 6 = 0 ✓
- Value at (1,2) = 5 XOR 2 XOR 1 XOR 1 XOR 6 XOR 3 = 2 ✓
Solution Implementation
1from typing import List
2from heapq import nlargest
3
4class Solution:
5 def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:
6 # Get dimensions of the matrix
7 rows, cols = len(matrix), len(matrix[0])
8
9 # Create a 2D prefix XOR array with padding for easier calculation
10 # prefix_xor[i][j] represents XOR of all elements in rectangle from (0,0) to (i-1,j-1)
11 prefix_xor = [[0] * (cols + 1) for _ in range(rows + 1)]
12
13 # Store all XOR values to find kth largest
14 all_xor_values = []
15
16 # Build prefix XOR array
17 for i in range(rows):
18 for j in range(cols):
19 # Calculate XOR value for current position using inclusion-exclusion principle
20 # Current = left XOR + top XOR - diagonal XOR + current element
21 # Since XOR is its own inverse: a ^ a = 0, we use XOR instead of addition/subtraction
22 prefix_xor[i + 1][j + 1] = (prefix_xor[i + 1][j] ^
23 prefix_xor[i][j + 1] ^
24 prefix_xor[i][j] ^
25 matrix[i][j])
26
27 # Add current XOR value to list
28 all_xor_values.append(prefix_xor[i + 1][j + 1])
29
30 # Find k largest elements and return the kth one (last element in the k largest)
31 return nlargest(k, all_xor_values)[-1]
32
1class Solution {
2 public int kthLargestValue(int[][] matrix, int k) {
3 int rows = matrix.length;
4 int cols = matrix[0].length;
5
6 // Create a 2D prefix XOR array with padding for easier calculation
7 // prefixXor[i][j] stores the XOR of all elements in rectangle from (0,0) to (i-1,j-1)
8 int[][] prefixXor = new int[rows + 1][cols + 1];
9
10 // List to store all XOR values for finding kth largest
11 List<Integer> xorValues = new ArrayList<>();
12
13 // Build prefix XOR array using dynamic programming
14 for (int i = 0; i < rows; i++) {
15 for (int j = 0; j < cols; j++) {
16 // Calculate XOR value for rectangle from (0,0) to (i,j)
17 // Formula: prefixXor[i+1][j+1] = prefixXor[i+1][j] XOR prefixXor[i][j+1] XOR prefixXor[i][j] XOR matrix[i][j]
18 // This works because XOR is self-inverse: a XOR a = 0
19 // The overlap area prefixXor[i][j] is XORed twice (from both prefixXor[i+1][j] and prefixXor[i][j+1])
20 // So it cancels out, and we need to XOR it once more along with the current element
21 prefixXor[i + 1][j + 1] = prefixXor[i + 1][j] ^ prefixXor[i][j + 1] ^ prefixXor[i][j] ^ matrix[i][j];
22
23 // Add the calculated XOR value to the list
24 xorValues.add(prefixXor[i + 1][j + 1]);
25 }
26 }
27
28 // Sort the XOR values in ascending order
29 Collections.sort(xorValues);
30
31 // Return the kth largest value (which is at index size - k in sorted ascending array)
32 return xorValues.get(xorValues.size() - k);
33 }
34}
35
1class Solution {
2public:
3 int kthLargestValue(vector<vector<int>>& matrix, int k) {
4 int rows = matrix.size();
5 int cols = matrix[0].size();
6
7 // Create a 2D prefix XOR array with padding to avoid boundary checks
8 // prefixXor[i][j] stores XOR of all elements in rectangle from (0,0) to (i-1,j-1)
9 vector<vector<int>> prefixXor(rows + 1, vector<int>(cols + 1, 0));
10
11 // Store all XOR values to find kth largest
12 vector<int> allXorValues;
13
14 // Build prefix XOR array and collect all XOR values
15 for (int i = 0; i < rows; ++i) {
16 for (int j = 0; j < cols; ++j) {
17 // Calculate XOR for rectangle from (0,0) to (i,j)
18 // Formula: XOR of current rectangle =
19 // XOR of left rectangle ^ XOR of top rectangle ^
20 // XOR of diagonal rectangle ^ current element
21 // The diagonal is subtracted (XORed) because it was counted twice
22 prefixXor[i + 1][j + 1] = prefixXor[i + 1][j] ^
23 prefixXor[i][j + 1] ^
24 prefixXor[i][j] ^
25 matrix[i][j];
26
27 // Add current XOR value to the collection
28 allXorValues.push_back(prefixXor[i + 1][j + 1]);
29 }
30 }
31
32 // Sort all XOR values in ascending order
33 sort(allXorValues.begin(), allXorValues.end());
34
35 // Return the kth largest value (which is at position size - k in sorted array)
36 return allXorValues[allXorValues.size() - k];
37 }
38};
39
1/**
2 * Finds the kth largest XOR value in a matrix where each cell contains
3 * the XOR of all elements in the rectangle from (0,0) to that cell
4 * @param matrix - The input matrix of numbers
5 * @param k - The position of the largest value to find (1-indexed)
6 * @returns The kth largest XOR value
7 */
8function kthLargestValue(matrix: number[][], k: number): number {
9 const rows: number = matrix.length;
10 const cols: number = matrix[0].length;
11
12 // Create a 2D prefix XOR array with padding for easier calculation
13 // prefixXor[i][j] represents XOR of all elements in rectangle from (0,0) to (i-1,j-1)
14 const prefixXor: number[][] = Array.from(
15 { length: rows + 1 },
16 () => Array.from({ length: cols + 1 }, () => 0)
17 );
18
19 // Store all XOR values to find kth largest
20 const xorValues: number[] = [];
21
22 // Calculate prefix XOR for each cell
23 for (let i = 0; i < rows; ++i) {
24 for (let j = 0; j < cols; ++j) {
25 // XOR formula: current cell = left XOR top XOR diagonal XOR matrix value
26 // The diagonal (top-left) is XORed twice (in left and top), so XOR it again to cancel out
27 prefixXor[i + 1][j + 1] = prefixXor[i + 1][j] ^ prefixXor[i][j + 1] ^
28 prefixXor[i][j] ^ matrix[i][j];
29
30 // Add the calculated XOR value to our collection
31 xorValues.push(prefixXor[i + 1][j + 1]);
32 }
33 }
34
35 // Sort XOR values in descending order
36 xorValues.sort((a: number, b: number) => b - a);
37
38 // Return the kth largest value (k is 1-indexed)
39 return xorValues[k - 1];
40}
41
Time and Space Complexity
Time Complexity: O(m × n × log(m × n))
or O(m × n)
depending on the implementation of nlargest
.
The algorithm consists of two main parts:
-
Computing XOR prefix sums: The nested loops iterate through all
m × n
elements of the matrix. For each element, we perform constant time XOR operations to computes[i + 1][j + 1]
. This takesO(m × n)
time. -
Finding the k-th largest value: The
nlargest(k, ans)
function is called on a list ofm × n
elements.- If
nlargest
uses a heap-based approach (which is typical in Python's heapq implementation), it maintains a min-heap of sizek
and processes allm × n
elements, resulting inO(m × n × log k)
time. Whenk
is close tom × n
, this becomesO(m × n × log(m × n))
. - If
nlargest
uses a quickselect-based approach, the average case would beO(m × n)
.
- If
Therefore, the overall time complexity is O(m × n × log(m × n))
in the worst case or O(m × n)
with an optimal selection algorithm.
Space Complexity: O(m × n)
The space usage includes:
- The 2D prefix sum array
s
of size(m + 1) × (n + 1)
, which isO(m × n)
. - The list
ans
that stores allm × n
XOR values, which isO(m × n)
. - The space used by
nlargest
which could beO(k)
for a heap-based approach orO(m × n)
if it creates a copy of the list.
The dominant space usage is O(m × n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding XOR Properties in Prefix Calculation
The Problem: A common mistake is treating XOR operations like regular addition when building the prefix array. Developers might incorrectly write:
# INCORRECT - treating XOR like addition prefix_xor[i+1][j+1] = prefix_xor[i+1][j] ^ prefix_xor[i][j+1] ^ matrix[i][j]
This misses the crucial prefix_xor[i][j]
term. Unlike addition where we subtract the overlapping region, with XOR we need to XOR it back because:
- The region
(0,0)
to(i-1, j-1)
is included in bothprefix_xor[i+1][j]
andprefix_xor[i][j+1]
- XORing the same value twice cancels it out:
a ^ a = 0
- We need to XOR it once more to restore it
The Solution: Always include all four terms in the prefix XOR calculation:
prefix_xor[i+1][j+1] = (prefix_xor[i+1][j] ^ prefix_xor[i][j+1] ^ prefix_xor[i][j] ^ # Don't forget this! matrix[i][j])
Pitfall 2: Using Wrong Method to Find k-th Largest
The Problem: Developers might sort the entire array and access the k-th element:
# INEFFICIENT - sorting entire array all_xor_values.sort(reverse=True) return all_xor_values[k-1]
This has O(mn*log(mn)) time complexity, which is unnecessary when k is small.
The Solution:
Use heapq.nlargest(k, all_xor_values)[-1]
which maintains a min-heap of size k, giving O(mn*log(k)) complexity. This is significantly better when k << mn.
Pitfall 3: Off-by-One Errors with Array Indexing
The Problem: Confusion between the padded prefix array indices and original matrix indices:
# INCORRECT - using wrong indices prefix_xor[i][j] = prefix_xor[i-1][j] ^ prefix_xor[i][j-1] ^ matrix[i][j]
This fails when i=0 or j=0 due to negative indices.
The Solution:
Consistently use the padding strategy where prefix_xor[i+1][j+1]
corresponds to matrix[i][j]
. This eliminates boundary condition checks and makes the code cleaner.
Which data structure is used in a depth first search?
Recommended Readings
https assets algo monster cover_photos dnd svg Divide and Conquer The main idea of divide and conquer is to split the main problem into two smaller components assuming that each one of the components had already been solved recursively and then try to solve the bigger problem using the solutions
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!