Facebook Pixel

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:

  1. Calculate the XOR value for every coordinate in the matrix
  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 Evaluator

Example 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:

  1. 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 compute s[i + 1][j + 1]. This takes O(m × n) time.

  2. Finding the k-th largest value: The nlargest(k, ans) function is called on a list of m × n elements.

    • If nlargest uses a heap-based approach (which is typical in Python's heapq implementation), it maintains a min-heap of size k and processes all m × n elements, resulting in O(m × n × log k) time. When k is close to m × n, this becomes O(m × n × log(m × n)).
    • If nlargest uses a quickselect-based approach, the average case would be O(m × n).

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:

  1. The 2D prefix sum array s of size (m + 1) × (n + 1), which is O(m × n).
  2. The list ans that stores all m × n XOR values, which is O(m × n).
  3. The space used by nlargest which could be O(k) for a heap-based approach or O(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 both prefix_xor[i+1][j] and prefix_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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which data structure is used in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More