Number of Submatrices That Sum to Target

本文解析了如何利用前缀和(prefixSum)技巧解决矩阵中子矩阵和为目标值的问题。通过构建2D数组的前缀和,理解其与原矩阵的关系,以及如何利用哈希表存储中间状态,有效地找出满足条件的子矩阵数量。适合理解和扩展到多维数组的动态规划问题解决策略。
摘要由CSDN通过智能技术生成

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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值