Number of Submatrices That Sum to Target
Question:
思路:
希望当我们做题的时候看到类似这种找matrix, submatrix summation 相关的题, 我们都能直接联想到prefixSum 这类题型。
对于matrix的input. 其实他本质上还是做prefixSum, 难点只是他由单维度升级成了多维度.
就是原先它是1D array prefixSum, 现在变成了2D array prefixSum, 解法还是一样的。可以理解成多了1D prefixSum上套了一个for loop
要理解这个题, 我们得举几个例子. 这道题的solution 解释的还可以. 可以参考
首先for example, we have a matrix
0 | 1 | 0
1 | 1 | 1
0 | 1 | 0
Think, how do we get a prefix matrix?
PrefixSum:
0 | 1 | 1
1 | 3 | 4
1 | 4 | 5
我们发现, prefixSum上面的每一个cell 对应的就是它左半边部分的cell的summation
就是prefixSum[1][1] = matrix[0][0] + matrix[0][1] + matrix[1][0] + matrix[1][1];
然后通过这个matrix, 我们就可以得到每一列或者每一行的matrix的summation
比如说我们想得到 row 1~2, col 1 ~ 2的matrix 的 sum.
1 | 0
1 | 1
我们只要用prefixSum[1][2] - prefixSum[1][0] 就可以了 = 4 - 1 = 3
想清楚这层关系之后, 就简单了. 那我们接下来的任务就是构建这个2D array 的 prefixSum.
首先我们想一个这个sum的规律.如果我们想要算prefixSum[1][1], 他是由什么构成的
是不是由它旁边两侧的prefixSum[0][1] 和 prefixSum[1][0] 构成的
前面我们提到了 prefixSum[1][1] = matrix[0][0] + matrix[0][1] + matrix[1][0] + matrix[1][1];
那么 prefixSum[0][1] = matrix[0][0] + matrix[0][1];
prefixSum[1][0] = matrix[0][0] + matrix[1][0];
所以prefixSum[0][1] + prefixSum[1][0] = matrix[0][0] + matrix[0][1] + matrix[0][0] + matrix[1][0];
加了两个matrix[0][0], 然后还差一个matrix[1][1]没加
所以 prefixSum[1][1] = prefixSum[0][1] + prefixSum[1][0] - matrix[0][0] + matrix[1][1];
接着注意, 因为prefixSum[I][j], 可以是很深的第i行和第j列. 所以按理来说
prefixSum[0][1] 应该 = prefixSum[0][0] + matrix[0][1]
prefixSum[1][0] 同理
所以说我们其实是加了两遍prefixSum[0][0]
so prefixSum[1][1] = prefixSum[0][1] + prefixSum[1][0] - prefixSum[0][0] + matrix[1][1];
同时这也满足当i和j很大时.
so prefixSum[i][j] = prefixSum[i-1][j] + prefixSum[I][j-1] - prefixSum[i-1][j-1] + matrix[i][j];
其次还有要注意一点就是我们需要创建一个m+1 x n+ 1大小的prefixSum matrix.
因为如果只是mxn大小的matrix. 我们没法通过相减得到first row 或者 second row的sumation
就是说 按照上面的例子
prefixSum[0][1] = prefixSum[-1][1] + prefixSum[0][0] - prefixSum[-1][0] + matrix[0][1]
但是array里没有-1index, 所以我们要做的是创建m+1 x n +1大小的2D array
接着assign row 1 和 col 1 为 all zero
也就是
prefixSum:
0 | 0 | 0 | 0
0 | 0 | 1 | 1
0 | 1 | 3 | 4
0 | 1 | 4 | 5
这样的话我们把原先的row 和 col 加一
prefixSum[0][1] 就等于现在的 prefixSum[1][2]
prefixSum[1][2] = prefixSum[0][2] + prefixSum[1][1] - prefixSum[0][1] + matrix[0][1]
注意因为matrix 还是原来的matrix. 所以matrix 的index 不变
那么我们的万能公式就变为了
prefixSum[i][j] = prefixSum[i-1][j] + prefixSum[I][j-1] - prefixSum[i-1][j-1] + matrix[i-1][j-1];
接下来 就和1D array 的 prefixSum题思路差不多了
我们只需要固定我们的行(固定列也可以)
这里我用固定row 来做解释
我们回来看我们创建的prefixSum:
0 | 0 | 0 | 0
0 | 0 | 1 | 1
0 | 1 | 3 | 4
0 | 1 | 4 | 5
我们观察发现, row3 col 3 - row 1 col 1 = 5 - 1 = 4 = row 2 ~ row 3 里面所有cell 的summation
我们就发现, 当固定了一个col 之后, 我们row 和 row 做prefixSum相减. 得到的结果就是row 之间所有cell的summation
同理row 3 col 3 - row 3 col 1 = 4 得到的是col 2 ~ col 3 的所有cell summation.
然后当我们写码的时候 我们只能固定一个, either col or row. 所以假设我们固定的是col.
那我们可以得到row 和 row 之间的summation, 却没法得到col 和 col 之间的
那么怎么做呢.
就像normal prefixSum 的题一样, 要么两个for loop 要么用hashmap 存之前的summation
所以这里当我们遍历每一个col的时候我们都把当前col上 row_i - row_j 的summation 存在hashMap 里. 存个数. 这样当我每次走到下一个col 的时候只要ask hashMap 我当前col summation的值和前面哪几个col summation相减可以= 我们的targetSum. 然后这就是结果
class Solution {
public int numSubmatrixSumTarget(int[][] matrix, int target) {
int[][] prefixSum = new int[matrix.length+1][matrix[0].length+1];
for(int i = 1;i < prefixSum.length; ++i){
for(int j = 1;j < prefixSum[0].length; ++j){
prefixSum[i][j] = prefixSum[i-1][j] + prefixSum[i][j-1] - prefixSum[i-1][j-1] + matrix[i-1][j-1];
}
}
HashMap<Integer, Integer> table = new HashMap<>();
int count = 0;
for(int r1 = 0; r1 < prefixSum.length; ++r1){
for(int r2 = r1+1; r2 < prefixSum.length; ++r2){
table.clear();
table.put(0,1);
for(int col = 1; col < prefixSum[0].length; ++col){
int curSum = prefixSum[r2][col] - prefixSum[r1][col];
if(table.containsKey(curSum - target)){
count += table.get(curSum - target);
}
table.put(curSum, table.getOrDefault(curSum, 0)+1);
}
}
}
return count;
}
}