这一题刚开始想的就是暴力解法,但是由于题目中给的数据量过大,如果用暴力解法的话,就是O(n^3)的时间复杂度,肯定超出了时间限制
在暴力解法中充斥着很多的重复计算,所以我们可以进一步想到用cur数组,来记录从nums[0]到nums[i]的sum和
即
cur[i] 记录的是nums[0] + nums[1] + ... +nums[i]的和
然后按照暴力解法,遍历每一个子数组,看看是否存在cur[j] - cur[i] == k
,存在则在最后返回的res上加一
但是不幸的是这个方法也是会时间超时,代码先贴上
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
vector<int> cur(nums.size() + 1 ,0);
int res = 0;
for( int i = 1; i <= nums.size(); ++ i){
cur[i] = cur[i-1] + nums[i-1];
}
for(int i = 0 ; i < cur.size(); ++ i){
for(int j = i+1; j < cur.size(); ++ j){
if(cur[j] - cur[i] == k)
res ++;
}
}
return res;
}
};
那怎么解决呢?于是我就查了一下,康到这个up主讲解的挺好的:这里
也就是方法三:其将方法二的所有的前缀和存储到一个map中
map中键表示前缀和,值表示出现的次数
再查找 j < i
时的前缀和中(即当前这个前缀和之前的元素中的前缀和,这一点很重要,不可以异步进行,不然测试用例 [1] 0过不了),是否有和等于sum-k
的,其个数即为和为k的个数
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
if(nums.empty()) return 0;
unordered_map<int,int> counts{{0,1}};
int sum = 0 ;
int ans = 0;
for(const int num : nums){
sum += num; //这一步和下一步不可以分开,这样就没法保证图片上的 j < i 了
ans += counts[sum - k];
++counts[sum];
}
return ans;
}
};
错误示范,没有将 sum += num; ans += counts[sum - k];
这两步放一起(即没有考虑 j < i ),会出现测试用例[1] 0不过:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int res = 0;
map<int,int> SumTimes;
// SumTimes.insert(make_pair(0,1)); //要插入一个为0的不然就会漏算
//但这样测试用例 [1] 0不太行
vector<int> cur(nums.size() + 1 ,0);
//注意数组越界
for( int i = 1; i < nums.size() + 1; ++ i){
cur[i] = cur[i-1] + nums[i-1];
}
for(int i = 1; i < cur.size() ; ++ i){
if( SumTimes.find(cur[i]) == SumTimes.end() )
SumTimes.insert(make_pair(cur[i],1));
else
SumTimes[cur[i]] ++;
}
//遍历map,看看有多少前缀为sum - k 的
for(int i = 1 ; i < cur.size() ; ++ i){ //这里不应该另起炉灶
if( cur[i] - k == 0 ){
res ++; continue;
}
// 即这里要求 j 要小于i 啊
//这里有问题啊 当测试用例为[1] 0的时候
if( SumTimes.find(cur[i] - k) != SumTimes.end() )
res += SumTimes[cur[i]];
}
return res;
}
};