Leetcode 1074. 元素和为目标值的子矩阵数量

1074 Leetcode 元素和为目标值的子矩阵数量

题目链接: 1074. 元素和为目标值的子矩阵数量

题目描述

给出矩阵 m a t r i x matrix matrix 和目标值 t a r g e t target target,返回元素总和等于目标值的非空子矩阵的数量。

子矩阵 x1, y1, x2, y2 是满足 x1 ≤ x ≤ x2 且 y1 ≤ y ≤ y2 的所有单元 m a t r i x [ x ] [ y ] matrix[x][y] matrix[x][y] 的集合。

如果 (x1, y1, x2, y2) 和 (x1’, y1’, x2’, y2’) 两个子矩阵中部分坐标不同(如:x1 != x1’),那么这两个子矩阵也不同。

样例
示例1:
输入:matrix = [[0,1,0],[1,1,1],[0,1,0]], target = 0
输出:4
解释:四个只含 01x1 子矩阵。
  
示例2:
输入:matrix = [[1,-1],[-1,1]], target = 0
输出:5
解释:两个 1x2 子矩阵,加上两个 2x1 子矩阵,再加上一个 2x2 子矩阵。

示例3:
输入:matrix = [[904]], target = 0
输出:0
提示:
  • 1 ≤ matrix.length ≤ 100
  • 1 ≤ matrix[0].length ≤ 100
  • -1000 ≤ matrix[i] ≤ 1000
  • − 1 0 8 ≤ t a r g e t ≤ 1 0 8 -10^8 \le target \le 10^8 108target108

算法1 (前缀和 + 哈希表)

思路分析

这里的话,我们可以看一个简单的例题的分析, 之前这个问题是一个一维的问题, 即在一个数组中找出区间和为k的区间的个数, 而这里这道问题转化为了二维的问题, 即在一个矩阵中找出一个子矩阵的和为target的子矩阵个数, 我们可以先看之前那道问题的分析思路, 然后再来看这道题, 题解链接:Leetcode 560.和为k的子数组, 然后再回过头来看这道题, 我们可以是否可以将问题再分,变为一个求一维数组的问题呢?如果可以那我们应该如何划分问题。 这里关键是如何枚举每一个矩阵, 这里我们采用上下界的方法来界定一组矩阵的范围, 即我们求以第i(0 ~ n - 1)行为上界, 以第j(i ~ n - 1) 为下界, 枚举这个区间内的矩形, 例如在以第i行为上界, 第j行为下界的范围内的矩阵(列数为m)区域中, 我们将这矩形区域中的每一列值加起来, 这样就得到一个m的数组,其数组的元素表示的以第i行为上界, 第j行为下界的矩阵中,在这个区域中每一列的和,这样我们对这个数组就可以把它当作一个一维问题的区间和来计算. 这样就变成了求一维数组中, 和为k的子数组问题. 之所以能这样转换, 是因为子矩阵要求也是一个连续的范围,故这样就对应一个连续的列. 同样每一个子矩阵都由i,j这两个上下界确定, 这样保证了我们列举的情况是不会漏的。 先枚举上界, 然后依次枚举下界. 第 j + 1 行的数组的更新, 只要将此时的数组的每一项, 加上 j + 1 行的每一项即可, 然后再求一次一维数组中的区间和为k的个数问题。

时间复杂度
C++ 代码
class Solution 
{
public:
    int subarraySum(vector<int>& nums, int k)            // 直接就是 560.和为k的子数组这道题的代码
    {
        int ss[110];  ss[0] = 0; 
        int res = 0, n = nums.size();
        for(int i = 1; i <= n; ++i) ss[i] = ss[i - 1] + nums[i - 1]; 
        unordered_map<int, int> m;
        m[0] = 1; 
        for(int i = 1; i <= n; ++i)
        {
            res += m[ss[i] - k]; 
            m[ss[i]]++; 
        }
        return res; 
    }

    int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) 
    {
        int res = 0, n = matrix.size(), m = matrix[0].size(); 
        for(int i = 0; i < n; ++i)                   // 枚举上边界 
        {
            vector<int> num(m, 0);                   // 存储每一列值的和的数组 
            for(int j = i; j < n; ++j)               // 枚举下边界
            { 
                for(int k = 0; k < m; ++k)  num[k] += matrix[j][k];   // 下界每往下移动, 更行数组
                res += subarraySum(num, target);                      // 求解num数组的区间和为target的个数
            }
        }
        return res; 
    }
};
C++ 代码改进版本
class Solution 
{
private:
    int subarraySum(vector<int> &nums, int k) 
    {
        unordered_map<int, int> mp;
        mp[0] = 1;
        int count = 0, pre = 0;
        
        for (auto &x:nums)        
        {
            pre += x;                      // 并没有像上面预处理出一个前缀和处理, 而是一起处理了
            
            //  这里用find查找下key是否存在, 而不是直接访问元素, 这样效率快很多. 故应该先判断
            // key 值存不存在, 这里的话查找key值近似O(1), 但如果直接访问value那就比找key效率慢点.  
            if (mp.find(pre - k) != mp.end())  count += mp[pre - k];  
            mp[pre]++;
        }
        return count;
    }

public:
    int numSubmatrixSumTarget(vector<vector<int>> &matrix, int target) 
    {
        int ans = 0;
        int m = matrix.size(), n = matrix[0].size();
        for (int i = 0; i < m; ++i) 
        { 
            vector<int> sum(n);
            for (int j = i; j < m; ++j) 
            { 
                for (int c = 0; c < n; ++c)  sum[c] += matrix[j][c]; 
                ans += subarraySum(sum, target);
            }
        }
        return ans;
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值