1074. Number of Submatrices that Sum to Target - Explanation
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
- Iterate over all pairs of rows
(r1, r2)defining vertical bounds. - For each row pair, iterate over all pairs of columns
(c1, c2)defining horizontal bounds. - Compute the sum of all elements within the submatrix bounds.
- If the sum equals the target, increment
res. - 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 resTime & Space Complexity
- Time complexity:
- Space complexity: extra space.
Where is the number of rows and 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
- Build a 2D prefix sum array where
subSum[r][c]represents the sum of all elements from(0,0)to(r,c). - 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].
- Compute its sum using:
- If the sum equals the target, increment the count.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and 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
- Compute the 2D prefix sum.
- For each pair of rows
(r1, r2):- Initialize a hash map with
{0: 1}to handle subarrays starting from column0. - Iterate through columns, computing the cumulative sum for the current row range.
- For each column, add the count of
curSum - targetfrom the map tores. - Update the map with the current sum.
- Initialize a hash map with
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and 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
- For each pair of columns
(c1, c2):- Maintain a row prefix array where
rowPrefix[r]stores the sum of elements in rowrfrom columnc1toc2. - Apply the 1D subarray sum technique: use a hash map to count prefix sums differing by the target.
- Maintain a row prefix array where
- For each row, update the row prefix and check against the hash map.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and 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.