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
解释:四个只含 0 的 1x1 子矩阵。
示例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 −108≤target≤108
算法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;
}
};