560. 和为 K 的子数组
#include <unordered_map>
class Solution {
public:int subarraySum(vector<int>& nums, int k) {int n=nums.size();unordered_map<int,int> hs;int sum=0,re=0;hs[0]=1;for(int i=0;i<n;i++){sum+=nums[i];if(hs.count(sum-k)) re+=hs[sum-k]; hs[sum]++;}return re;}
};
求和为k的子数组,我们先求以下标i为结尾的子数组和==k的情况,即求前缀和==sum[i]-k的情况有多少种
1.在[0,i-1]区间内,有多少前缀和==sum[i]-k.用unordered_map<int,int>,第一个int记录前缀和,第二个int记录相同前缀和的次数。
2.对于sum[i]==k,即[0,i]整个区间的情况。这种情况前缀和==0,需要特殊处理hs[0]=1
假设我们有一个数组[k,2k,3k]在遍历到第一个元素时,sum正好等于k。如果没有初始化hs[0]=1此时hs[0]不存在于哈希表中,导致算法无法将这段部分和计入结果。
974. 和可被 K 整除的子数组
这道题和上面题一样,上一道题哈希表存入的是sum-前缀和。
这道题是什么呢?还是要看sum当前位置前缀和 x前缀和 k三者的关系。
求一段连续区间可被K整除,也是先以i为该区间的结尾,求紫区间是否可被K整除,就转化为求在哈希表中当前sum除k相同余数。因为sum可能为负数,不能在哈希表中映射要变为正数。(sum%k+k)/k
class Solution {
public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int, int> hash;hash[0 % k] = 1; // 0 这个数的余数int sum = 0, ret = 0;for (auto x : nums) {sum += x; // 算出当前位置的前缀和int r = (sum % k + k) % k; // 修正后的余数if (hash.count(r))ret += hash[r]; // 统计结果hash[r]++;}return ret;}
};
525. 连续数组
把数组里面0全变为-1,就变相于求和为0的最长子字符串。
在i下标前找前缀和==sum[i]的下标,哈希表<前缀和,下标>.如果是相同的前缀和就不用更换下标,因为原本记录的下标距离i最远,和为k的子数组更长。
前缀和==0的情况,把下标设为-1
class Solution {
public:int findMaxLength(vector<int>& nums) {for(auto &o:nums)if(o==0) o=-1;int n=nums.size(),sum=0;int re=0;unordered_map<int,int> hs;hs[0]=-1;for(int i=0;i<n;i++){sum+=nums[i];if(hs.count(sum))re=max(i-hs[sum],re);else hs[sum]=i;}return re;}
};
1314. 矩阵区域和
answer[i][j]值 为以mat[i][j]为中心向四周扩张k个元素的长度,所圈起元素的和。
eg.answer[1][1]值 为以mat[1][1]==5为中心向四周扩张1个元素的长度,即1+2+3+...+9的和
解法:二维数组前缀和
求二维数组前缀和创建的数组,一般在原数组的行和列再加1.便于处理边界问题。
还记得怎么求二维数组前缀和吗?
dp[i][j]的值为上面图像的整个面积。
dp[i][j]=dp[i][j-1](左边面积)+dp[i-1][j](上面面积)-dp[i-][j-1](A多余的面积)+mat[i][j](右小块的面积)
怎么用二维数组前缀和来求下图右下角面积?
我们有了二维数组前缀和,只需要知道x1,y1,x2,y2下标就可以了。
右下加面积=dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1]
现在我们有了二维数组前缀和,就可以求mat二维数组中任意一块的元素和。
求所圈起元素的和,我们就需要知道该图形的左上角(x1,y1),右下角(x2,y2),
x1=i-k x2=i+k y1=j-k y2=j+k
但不能超出数组范围(m行n列)
所以x1=max(0,i-k) x2=min(m-1,i+k) y1=max(0,j-k) y2=min(n-1,j+k)
但是我们dp[][]是从下标1开始填入有效值,mat数组从0开始,我们就需要+1建立映射关系。
所以x1=max(0,i-k)+1 x2=min(m-1,i+k)+1 y1=max(0,j-k)+1 y2=min(n-1,j+k)+1
class Solution {
public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {int m = mat.size(), n = mat[0].size();vector<vector<int>> dp(m + 1, vector<int>(n + 1));// 1. 预处理前缀和矩阵for (int i = 1; i <= m; i++)for (int j = 1; j <= n; j++)dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] +mat[i - 1][j - 1];// 2. 使⽤vector<vector<int>> ret(m, vector<int>(n));for (int i = 0; i < m; i++)for (int j = 0; j < n; j++) {int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] +dp[x1 - 1][y1 - 1];}return ret;}
};