1074. Number of Submatrices that Sum to Target - Explanation

Problem Link



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Prefix Sum (1D and 2D) - Used to efficiently compute subarray/submatrix sums in O(1) time after preprocessing
  • Hash Map - Used to count prefix sums and find pairs that differ by the target value
  • Subarray Sum Equals K Pattern - The optimal solution reduces the 2D problem to multiple 1D subarray sum problems

1. Brute Force

Intuition

We consider every possible submatrix by fixing all four corners (top-left and bottom-right). For each submatrix, we sum all its elements and check if it equals the target. This is the most straightforward approach but very slow.

Algorithm

  1. Iterate over all pairs of rows (r1, r2) defining vertical bounds.
  2. For each row pair, iterate over all pairs of columns (c1, c2) defining horizontal bounds.
  3. Compute the sum of all elements within the submatrix bounds.
  4. If the sum equals the target, increment res.
  5. Return the total count.
class Solution:
    def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        res = 0

        for r1 in range(ROWS):
            for r2 in range(r1, ROWS):
                for c1 in range(COLS):
                    for c2 in range(c1, COLS):
                        subSum = 0
                        for r in range(r1, r2 + 1):
                            for c in range(c1, c2 + 1):
                                subSum += matrix[r][c]

                        if subSum == target:
                            res += 1

        return res

Time & Space Complexity

  • Time complexity: O(m3n3)O(m ^ 3 * n ^ 3)
  • Space complexity: O(1)O(1) extra space.

Where mm is the number of rows and nn is the number of columns of the given matrix.


2. Two Dimensional Prefix Sum

Intuition

By precomputing a 2D prefix sum, any submatrix sum can be calculated in O(1) using inclusion-exclusion. We still enumerate all submatrix boundaries, but computing each sum is now constant time instead of proportional to the submatrix size.

Algorithm

  1. Build a 2D prefix sum array where subSum[r][c] represents the sum of all elements from (0,0) to (r,c).
  2. For each submatrix defined by corners (r1, c1) and (r2, c2):
    • Compute its sum using: subSum[r2][c2] - subSum[r1-1][c2] - subSum[r2][c1-1] + subSum[r1-1][c1-1].
  3. If the sum equals the target, increment the count.
  4. Return the total count.
class Solution:
    def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        sub_sum = [[0] * COLS for _ in range(ROWS)]

        for r in range(ROWS):
            for c in range(COLS):
                top = sub_sum[r - 1][c] if r > 0 else 0
                left = sub_sum[r][c - 1] if c > 0 else 0
                top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0
                sub_sum[r][c] = matrix[r][c] + top + left - top_left

        res = 0
        for r1 in range(ROWS):
            for r2 in range(r1, ROWS):
                for c1 in range(COLS):
                    for c2 in range(c1, COLS):
                        top = sub_sum[r1 - 1][c2] if r1 > 0 else 0
                        left = sub_sum[r2][c1 - 1] if c1 > 0 else 0
                        top_left = sub_sum[r1 - 1][c1 - 1] if min(r1, c1) > 0 else 0
                        cur_sum = sub_sum[r2][c2] - top - left + top_left
                        if cur_sum == target:
                            res += 1
        return res

Time & Space Complexity

  • Time complexity: O(m2n2)O(m ^ 2 * n ^ 2)
  • Space complexity: O(mn)O(m * n)

Where mm is the number of rows and nn is the number of columns of the given matrix.


3. Horizontal 1D Prefix Sum

Intuition

We reduce the 2D problem to multiple 1D subarray sum problems. After fixing a row range (r1 to r2), the submatrix sum becomes a horizontal prefix sum across columns. We apply the classic technique of using a hash map to count how many previous prefix sums differ by exactly the target.

Algorithm

  1. Compute the 2D prefix sum.
  2. For each pair of rows (r1, r2):
    • Initialize a hash map with {0: 1} to handle subarrays starting from column 0.
    • Iterate through columns, computing the cumulative sum for the current row range.
    • For each column, add the count of curSum - target from the map to res.
    • Update the map with the current sum.
  3. Return the total count.
class Solution:
    def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        sub_sum = [[0] * COLS for _ in range(ROWS)]

        for r in range(ROWS):
            for c in range(COLS):
                top = sub_sum[r - 1][c] if r > 0 else 0
                left = sub_sum[r][c - 1] if c > 0 else 0
                top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0
                sub_sum[r][c] = matrix[r][c] + top + left - top_left

        res = 0
        for r1 in range(ROWS):
            for r2 in range(r1, ROWS):
                count = defaultdict(int)
                count[0] = 1
                for c in range(COLS):
                    cur_sum = sub_sum[r2][c] - (sub_sum[r1 - 1][c] if r1 > 0 else 0)
                    res += count[cur_sum - target]
                    count[cur_sum] += 1

        return res

Time & Space Complexity

  • Time complexity: O(m2n)O(m ^ 2 * n)
  • Space complexity: O(mn)O(m * n)

Where mm is the number of rows and nn is the number of columns of the given matrix.


4. Vertical 1D Prefix Sum

Intuition

Similar to the horizontal approach, but we fix column bounds instead of row bounds. For each pair of columns (c1 to c2), we maintain a running row sum and apply the hash map technique vertically. This can be more efficient when there are fewer columns than rows.

Algorithm

  1. For each pair of columns (c1, c2):
    • Maintain a row prefix array where rowPrefix[r] stores the sum of elements in row r from column c1 to c2.
    • Apply the 1D subarray sum technique: use a hash map to count prefix sums differing by the target.
  2. For each row, update the row prefix and check against the hash map.
  3. Return the total count.
class Solution:
    def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        res = 0

        for c1 in range(COLS):
            row_prefix = [0] * ROWS
            for c2 in range(c1, COLS):
                for r in range(ROWS):
                    row_prefix[r] += matrix[r][c2]

                count = defaultdict(int)
                count[0] = 1
                cur_sum = 0
                for r in range(ROWS):
                    cur_sum += row_prefix[r]
                    res += count[cur_sum - target]
                    count[cur_sum] += 1

        return res

Time & Space Complexity

  • Time complexity: O(mn2)O(m * n ^ 2)
  • Space complexity: O(m)O(m)

Where mm is the number of rows and nn is the number of columns of the given matrix.


Common Pitfalls

Incorrect 2D Prefix Sum Formula

The inclusion-exclusion principle for 2D prefix sums is error-prone. The correct formula is subSum[r2][c2] - subSum[r1-1][c2] - subSum[r2][c1-1] + subSum[r1-1][c1-1]. Forgetting to add back the top-left corner (which gets subtracted twice) leads to incorrect results.

Off-by-One Errors with Boundary Conditions

When computing prefix sums or querying submatrix sums, accessing indices like r1-1 or c1-1 when r1=0 or c1=0 causes out-of-bounds errors. You must handle these boundary cases explicitly by checking if the index is valid before accessing.

Forgetting to Initialize Hash Map with Zero

In the 1D subarray sum reduction, the hash map must be initialized with {0: 1} to count subarrays starting from the beginning of the row range. Without this initialization, you miss all submatrices whose sum equals exactly the target from the leftmost column.

Choosing the Wrong Dimension to Iterate

The optimal approach iterates over the smaller dimension for the outer loops. If you iterate over rows when there are many more rows than columns, the time complexity becomes worse than necessary. Always consider iterating over min(m, n) for the pair loops.

Integer Overflow with Large Matrices

When the matrix contains large values (positive or negative), prefix sums can overflow 32-bit integers. Use 64-bit integers or appropriate data types to handle cumulative sums, especially when the matrix is large.