leetcode560.和为K的子数组
算法
前缀和+哈希
时间复杂度O(n)
。
在解决这道题前需要先清楚,一个和为k
的子数组即为一对前缀和的差值。
1.我们假设有这么一个子数组[i,j]
满足数字和为k
,那么就有pre[j] - pre[i-1] = k
(注:pre数组为记录前缀和的数组),则pre[i-1] = pre[j] - k
;
2.题目问找到nums数组中和为k的连续的子数组的数目,一开始想到是否会重叠,后来仔细看题目后发现题目中并没有限定子数组是否重叠,那么这道题只需要记录pre[i-1]
出现的次数即可;
3.不过为什么要累加pre[i-1]
出现的次数呢?
原因是我们要算出满足以下标为j
为结尾的数组的数字和为k
的出现的次数,有pre[i-1] = pre[j] - k
可知[i,j]
这个子数组已经满足数字和为k
,那么首先需要想到的是应该把这一次记录上,即+1
,不过由于在遍历时我们每次都会把当前的前缀和用一个哈希表存储下来,并且累加1,这里我们每次都累加1,也就是说在下标i
前面有可能存在一个下标k
,满足[k,i]
这个子数组的数字和为k
,这样就不难理解为什么每次都需要累加pre[i-1]
的次数了.(注意这个pre[i-1]
的值是通过pre[j]-k
来得到的)
C++代码
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int len = nums.size();
int sum = 0; //记录当前位置的前缀和
int res = 0;
unordered_map<int,int> hs;
hs[0] = 1; //表示和为0出现1次
for(int i = 0; i < len; i++){
sum += nums[i];
if(hs.find(sum - k) != hs.end()){
res += hs[sum-k];
}
hs[sum]++;
}
return res;
}
};