1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public int numSubmatrixSumTarget(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
for (int i = 0; i < m; i++) {
for (int j = 1; j < n; j++) {
matrix[i][j] += matrix[i][j - 1];
}
}
int ans = 0;
Map<Integer, Integer> sumCount = new HashMap<>();
// i determines the start row
// j determines the width
// k determines the height
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
sumCount.clear();
int currSum = 0;
for (int k = 0; k < m; k++) {
currSum += matrix[k][j] - (i > 0 ? matrix[k][i - 1] : 0);
if (currSum == target) {
ans += 1;
}
int counterPart = currSum - target;
ans += sumCount.getOrDefault(counterPart, 0);
sumCount.put(currSum, sumCount.getOrDefault(currSum, 0) + 1);
}
}
}
return ans;
}
}

这道题可以简化为prefix sum. 思路就是把一些column叠起来. 比如让第0列和第1列叠起来, 此时我们在选任意一行, 比如第3行, 那么此时就构成了一个4 * 2的矩形. 这里叠起来的意思就是让重合的位置的值相加.

那么对于左上角在第0列的宽为2的矩形, 我们就可以让第0列和第1列叠起来, 然后像计算prefix sum那样从上到下遍历. 到一个地方记录此时的sum是多少, 然后看之前有没有是sum - k的sum.

有这个思路之后, 我们还有个问题就是比如说我想求左上角在第1列的矩形有哪些可以凑成k. 比如让第1列和第2列叠起来, 此时我们求左上角在第1列宽度为2的矩形有没有可以凑成k的. 此时我们要用到第1列 + 第二列的sum. 之前我们有用到第0列 + 第1列的sum. 于是我们可以先横向求prefix sum, 也就是每一个row的prefix sum先求出来. 然后当要遍历左上角在某个列的时候,j来表示此时的宽度, 我们就能快速得出i和j之间的sum是多少. 也就是i到j之间列叠起来后的sum是多少(就是0到j处的sum - 0到i处的sum).

感觉这道题话语很难描述清楚.

时间复杂度: O(m * n *n)
空间复杂度: O(m) 就是那个hashmap存叠起来后压成的array的prefix sum