Facebook Pixel

3070. Count Submatrices with Top-Left Element and Sum Less Than k

Problem Description

You are given a 2D integer matrix grid (0-indexed) and an integer k.

The task is to count how many submatrices meet two conditions:

  1. The submatrix must contain the top-left element of the grid (element at position [0][0])
  2. The sum of all elements in the submatrix must be less than or equal to k

A submatrix is defined by selecting a rectangular region from the original matrix. Since all valid submatrices must contain the top-left element grid[0][0], they all start from position [0][0] and extend to some position [i][j] where i and j represent the bottom-right corner of the submatrix.

For example, if you have a 3x3 grid, possible submatrices that contain the top-left element would include:

  • Just the single element at [0][0]
  • A 1x2 rectangle from [0][0] to [0][1]
  • A 2x1 rectangle from [0][0] to [1][0]
  • A 2x2 rectangle from [0][0] to [1][1]
  • And so on, up to the entire grid

The solution uses a two-dimensional prefix sum approach where s[i][j] represents the sum of all elements in the submatrix from [0][0] to [i-1][j-1]. The prefix sum is calculated using the formula:

s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + x

where x is the current element at position [i-1][j-1] in the original grid. Since each prefix sum represents a valid submatrix starting from the top-left corner, we simply count how many of these sums are less than or equal to k.

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

Intuition

The key insight is recognizing that since all valid submatrices must contain the top-left element grid[0][0], every submatrix we need to consider has a fixed starting point at [0][0] and extends to some position [i][j].

This constraint simplifies our problem significantly. Instead of considering all possible submatrices in the grid (which would require checking different starting and ending points), we only need to consider submatrices that start from the top-left corner. This means for an m x n grid, we have exactly m * n possible submatrices to check.

The natural approach would be to calculate the sum for each of these submatrices individually, but that would be inefficient. For each submatrix ending at [i][j], we'd need to sum all elements from [0][0] to [i][j], leading to repeated calculations.

This is where the prefix sum technique becomes valuable. As we iterate through the grid from top-left to bottom-right, we can build up the sum incrementally. When we're at position [i][j], we already know:

  • The sum of the submatrix from [0][0] to [i-1][j] (the rectangle directly above)
  • The sum of the submatrix from [0][0] to [i][j-1] (the rectangle to the left)
  • The sum of the submatrix from [0][0] to [i-1][j-1] (the overlapping rectangle)

Using these previously calculated values, we can compute the sum for the submatrix ending at [i][j] by adding the current element, adding the sums from above and left, and subtracting the overlap that was counted twice: s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + current_element.

Since each prefix sum s[i][j] represents exactly one valid submatrix (from [0][0] to [i][j]), we can simply check if this sum is less than or equal to k and increment our counter accordingly. This gives us an efficient solution that processes each element only once.

Learn more about Prefix Sum patterns.

Solution Approach

The implementation uses a two-dimensional prefix sum approach to efficiently calculate the sum of all submatrices starting from the top-left corner.

Data Structure Setup: We create a 2D array s with dimensions (m+1) x (n+1) where m and n are the dimensions of the original grid. The extra row and column (initialized to 0) serve as padding to handle boundary cases elegantly, eliminating the need for special checks when accessing s[i-1][j] or s[i][j-1].

Algorithm Implementation:

  1. Initialize the prefix sum array s with all zeros and a counter ans to track valid submatrices.

  2. Iterate through each element of the grid using nested loops. We use enumerate(grid, 1) and enumerate(row, 1) to get 1-indexed positions, which align with our padded prefix sum array.

  3. For each position (i, j), calculate the prefix sum using the formula:

    s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + x

    Where:

    • s[i-1][j] is the sum of the submatrix from [0][0] to [i-2][j-1]
    • s[i][j-1] is the sum of the submatrix from [0][0] to [i-1][j-2]
    • s[i-1][j-1] is the overlapping region that gets counted twice
    • x is the current element grid[i-1][j-1]
  4. After calculating each prefix sum, immediately check if s[i][j] <= k. If true, increment the answer counter. This works because each s[i][j] represents exactly one submatrix starting from [0][0] and ending at [i-1][j-1] in the original grid.

Time and Space Complexity:

  • Time: O(m * n) where we visit each element exactly once
  • Space: O(m * n) for the prefix sum array

The elegance of this solution lies in combining the prefix sum calculation with the counting logic in a single pass, making it both efficient and concise.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate the solution approach.

Consider a 3x3 grid with k = 7:

grid = [[1, 1, 1],
        [1, 1, 1],
        [1, 1, 1]]
k = 7

We'll create a prefix sum array s with dimensions 4x4 (padded with zeros):

s = [[0, 0, 0, 0],
     [0, 0, 0, 0],
     [0, 0, 0, 0],
     [0, 0, 0, 0]]

Step-by-step calculation:

Position [0][0] (grid value = 1):

  • Calculate: s[1][1] = s[0][1] + s[1][0] - s[0][0] + 1 = 0 + 0 - 0 + 1 = 1
  • This represents the submatrix containing only element [0][0]
  • Since 1 ≤ 7, increment counter: ans = 1

Position [0][1] (grid value = 1):

  • Calculate: s[1][2] = s[0][2] + s[1][1] - s[0][1] + 1 = 0 + 1 - 0 + 1 = 2
  • This represents the 1x2 submatrix from [0][0] to [0][1]
  • Since 2 ≤ 7, increment counter: ans = 2

Position [0][2] (grid value = 1):

  • Calculate: s[1][3] = s[0][3] + s[1][2] - s[0][2] + 1 = 0 + 2 - 0 + 1 = 3
  • This represents the 1x3 submatrix from [0][0] to [0][2]
  • Since 3 ≤ 7, increment counter: ans = 3

Position [1][0] (grid value = 1):

  • Calculate: s[2][1] = s[1][1] + s[2][0] - s[1][0] + 1 = 1 + 0 - 0 + 1 = 2
  • This represents the 2x1 submatrix from [0][0] to [1][0]
  • Since 2 ≤ 7, increment counter: ans = 4

Position [1][1] (grid value = 1):

  • Calculate: s[2][2] = s[1][2] + s[2][1] - s[1][1] + 1 = 2 + 2 - 1 + 1 = 4
  • This represents the 2x2 submatrix from [0][0] to [1][1]
  • Since 4 ≤ 7, increment counter: ans = 5

Position [1][2] (grid value = 1):

  • Calculate: s[2][3] = s[1][3] + s[2][2] - s[1][2] + 1 = 3 + 4 - 2 + 1 = 6
  • This represents the 2x3 submatrix from [0][0] to [1][2]
  • Since 6 ≤ 7, increment counter: ans = 6

Position [2][0] (grid value = 1):

  • Calculate: s[3][1] = s[2][1] + s[3][0] - s[2][0] + 1 = 2 + 0 - 0 + 1 = 3
  • This represents the 3x1 submatrix from [0][0] to [2][0]
  • Since 3 ≤ 7, increment counter: ans = 7

Position [2][1] (grid value = 1):

  • Calculate: s[3][2] = s[2][2] + s[3][1] - s[2][1] + 1 = 4 + 3 - 2 + 1 = 6
  • This represents the 3x2 submatrix from [0][0] to [2][1]
  • Since 6 ≤ 7, increment counter: ans = 8

Position [2][2] (grid value = 1):

  • Calculate: s[3][3] = s[2][3] + s[3][2] - s[2][2] + 1 = 6 + 6 - 4 + 1 = 9
  • This represents the entire 3x3 grid
  • Since 9 > 7, do not increment counter: ans = 8

Final answer: 8 submatrices have sum ≤ 7

Solution Implementation

1class Solution:
2    def countSubmatrices(self, grid: List[List[int]], k: int) -> int:
3        # Get dimensions of the grid
4        rows = len(grid)
5        cols = len(grid[0])
6      
7        # Create prefix sum array with padding for easier calculation
8        # prefix_sum[i][j] represents sum of all elements in submatrix from (0,0) to (i-1,j-1)
9        prefix_sum = [[0] * (cols + 1) for _ in range(rows + 1)]
10      
11        # Counter for valid submatrices
12        count = 0
13      
14        # Build prefix sum array and count valid submatrices
15        for i in range(1, rows + 1):
16            for j in range(1, cols + 1):
17                # Calculate prefix sum using inclusion-exclusion principle
18                # Sum = sum_above + sum_left - sum_diagonal + current_element
19                prefix_sum[i][j] = (prefix_sum[i - 1][j] + 
20                                   prefix_sum[i][j - 1] - 
21                                   prefix_sum[i - 1][j - 1] + 
22                                   grid[i - 1][j - 1])
23              
24                # Check if submatrix from (0,0) to current position has sum <= k
25                if prefix_sum[i][j] <= k:
26                    count += 1
27      
28        return count
29
1class Solution {
2    public int countSubmatrices(int[][] grid, int k) {
3        int rows = grid.length;
4        int cols = grid[0].length;
5      
6        // Create a 2D prefix sum array with padding for easier calculation
7        // prefixSum[i][j] represents the sum of all elements in the submatrix 
8        // from (0,0) to (i-1, j-1) in the original grid
9        int[][] prefixSum = new int[rows + 1][cols + 1];
10      
11        int count = 0;
12      
13        // Build prefix sum array and count valid submatrices
14        for (int i = 1; i <= rows; i++) {
15            for (int j = 1; j <= cols; j++) {
16                // Calculate prefix sum using inclusion-exclusion principle:
17                // Add the sum from top and left, subtract the overlapping top-left,
18                // then add the current element
19                prefixSum[i][j] = prefixSum[i - 1][j]           // Sum from top
20                                + prefixSum[i][j - 1]           // Sum from left
21                                - prefixSum[i - 1][j - 1]       // Remove overlap
22                                + grid[i - 1][j - 1];           // Add current element
23              
24                // If the sum of submatrix from (0,0) to current position is <= k,
25                // increment the counter
26                if (prefixSum[i][j] <= k) {
27                    count++;
28                }
29            }
30        }
31      
32        return count;
33    }
34}
35
1class Solution {
2public:
3    int countSubmatrices(vector<vector<int>>& grid, int k) {
4        // Get dimensions of the grid
5        int rows = grid.size();
6        int cols = grid[0].size();
7      
8        // Create a 2D prefix sum array with padding
9        // prefixSum[i][j] represents the sum of all elements in the submatrix 
10        // from (0,0) to (i-1,j-1) in the original grid
11        vector<vector<int>> prefixSum(rows + 1, vector<int>(cols + 1, 0));
12      
13        // Counter for valid submatrices
14        int count = 0;
15      
16        // Build prefix sum array and count valid submatrices
17        for (int i = 1; i <= rows; ++i) {
18            for (int j = 1; j <= cols; ++j) {
19                // Calculate prefix sum using inclusion-exclusion principle
20                // Current sum = sum above + sum to the left - overlap + current element
21                prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] 
22                                 - prefixSum[i - 1][j - 1] + grid[i - 1][j - 1];
23              
24                // Check if the sum of submatrix from (0,0) to current position is <= k
25                if (prefixSum[i][j] <= k) {
26                    ++count;
27                }
28            }
29        }
30      
31        return count;
32    }
33};
34
1/**
2 * Counts the number of submatrices with top-left corner at (0,0) 
3 * whose sum is less than or equal to k
4 * @param grid - The input 2D grid of numbers
5 * @param k - The maximum sum threshold
6 * @returns The count of valid submatrices
7 */
8function countSubmatrices(grid: number[][], k: number): number {
9    const rows: number = grid.length;
10    const cols: number = grid[0].length;
11  
12    // Create a 2D prefix sum array with padding for easier calculation
13    // prefixSum[i][j] represents the sum of all elements in the submatrix 
14    // from (0,0) to (i-1, j-1) in the original grid
15    const prefixSum: number[][] = Array.from(
16        { length: rows + 1 }, 
17        () => Array(cols + 1).fill(0)
18    );
19  
20    let count: number = 0;
21  
22    // Build prefix sum array and count valid submatrices
23    for (let row = 1; row <= rows; row++) {
24        for (let col = 1; col <= cols; col++) {
25            // Calculate prefix sum using the inclusion-exclusion principle
26            // Current sum = sum above + sum to the left - overlap + current element
27            prefixSum[row][col] = prefixSum[row - 1][col] 
28                                 + prefixSum[row][col - 1] 
29                                 - prefixSum[row - 1][col - 1] 
30                                 + grid[row - 1][col - 1];
31          
32            // Check if the submatrix from (0,0) to current position has sum <= k
33            if (prefixSum[row][col] <= k) {
34                count++;
35            }
36        }
37    }
38  
39    return count;
40}
41

Time and Space Complexity

The time complexity is O(m × n), where m is the number of rows and n is the number of columns of the matrix. This is because the algorithm uses two nested loops that iterate through each element of the grid exactly once. The outer loop runs m times (for each row), and the inner loop runs n times (for each column). All operations inside the loops (array access, arithmetic operations, and comparison) take constant time O(1).

The space complexity is O(m × n), where m and n are the dimensions of the input matrix. This is due to the 2D prefix sum array s that is created with dimensions (m + 1) × (n + 1). The extra row and column are added to handle boundary cases, but the space requirement is still proportional to the size of the input grid. The only additional space used is for the loop variables and the answer counter, which require O(1) space.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Index Mapping Between Grid and Prefix Sum Array

One of the most common mistakes is confusion about the index mapping between the original grid and the padded prefix sum array. Since the prefix sum array has an extra row and column for padding, prefix_sum[i][j] corresponds to the sum from grid[0][0] to grid[i-1][j-1], not grid[i][j].

Incorrect Implementation:

# Wrong: directly using grid indices for prefix_sum
prefix_sum[i][j] = (prefix_sum[i-1][j] + 
                   prefix_sum[i][j-1] - 
                   prefix_sum[i-1][j-1] + 
                   grid[i][j])  # Error: index out of bounds when i=rows or j=cols

Correct Implementation:

# Correct: adjusting indices properly
prefix_sum[i][j] = (prefix_sum[i-1][j] + 
                   prefix_sum[i][j-1] - 
                   prefix_sum[i-1][j-1] + 
                   grid[i-1][j-1])  # Correct: maps to actual grid element

2. Forgetting the Inclusion-Exclusion Principle

Another frequent error is forgetting to subtract the overlapping region prefix_sum[i-1][j-1] when calculating the prefix sum. This leads to double-counting the rectangular region that appears in both prefix_sum[i-1][j] and prefix_sum[i][j-1].

Incorrect Implementation:

# Wrong: missing subtraction of overlapping area
prefix_sum[i][j] = (prefix_sum[i-1][j] + 
                   prefix_sum[i][j-1] + 
                   grid[i-1][j-1])  # Error: double-counts the overlap

Correct Implementation:

# Correct: properly applying inclusion-exclusion
prefix_sum[i][j] = (prefix_sum[i-1][j] + 
                   prefix_sum[i][j-1] - 
                   prefix_sum[i-1][j-1] +  # Subtract the overlap
                   grid[i-1][j-1])

3. Misunderstanding the Problem Requirements

Some might incorrectly interpret the problem as finding ALL possible submatrices in the grid, not just those starting from the top-left corner [0][0].

Incorrect Approach:

# Wrong: counting all possible submatrices
count = 0
for i1 in range(rows):
    for j1 in range(cols):
        for i2 in range(i1, rows):
            for j2 in range(j1, cols):
                # Calculate sum of submatrix from (i1,j1) to (i2,j2)
                # This counts ALL submatrices, not just those from (0,0)

Correct Understanding: Since all valid submatrices must contain grid[0][0], they all start from position [0][0]. Each prefix sum prefix_sum[i][j] represents exactly one such submatrix, making the solution much simpler.

4. Off-by-One Errors in Loop Ranges

Using incorrect loop ranges can lead to missing elements or accessing out-of-bounds indices.

Incorrect Implementation:

# Wrong: loop ranges don't cover all elements
for i in range(rows):  # Should be range(1, rows + 1) for prefix_sum
    for j in range(cols):  # Should be range(1, cols + 1) for prefix_sum
        prefix_sum[i][j] = ...  # This misses the padding logic

Correct Implementation:

# Correct: proper ranges for 1-indexed prefix sum array
for i in range(1, rows + 1):
    for j in range(1, cols + 1):
        prefix_sum[i][j] = ...

These pitfalls highlight the importance of understanding the relationship between array indices, the mathematical formula for 2D prefix sums, and the specific problem constraints.

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

How many ways can you arrange the three letters A, B and C?


Recommended Readings

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

Load More