题目
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
思路分析
看到题目后,觉得和kmp很相似,猜测用前缀和能实现最小时间复杂度。但是搞忘了前缀和是啥。后面试着通过遍历nums数组,用sum记录从i到j的nums之和,再通过对sum和k的大小关系进行判断。如果sum小于k,则累加到j,使得sum>=k为止,若sum=k,则ans++,若sum>k,则i=pre+1,sum-=nums[pre],i等于pre,再次进入循环。
但是这种做法是有很大的问题的,他默认所有数都是正数,即当累计和大于k的时候,就将i++,sum减少一个即可。但是实际上nums可以为负数。
后面又通过取nums中的最小负数为min,将其每个数都加上-min,然后在和k进行判断的时候给k那一项加上(j-i+1)*-min进行比较。不过这样也没做出来。
后面看了题解,果然,那种遍历的方法时间复杂度得有O(n^2),会TLE。官方题解采用的是前缀和加哈希表。
官方题解思路回顾
设前缀和为pres[],我们想要的效果是pres[j]-pres[i]=k。如果通过for循环依次判断,时间复杂度还是O(n^2),这道题本身不关注是第ij项之差能实现累计和为k,于是通过引入哈希表unordered_map<int,int> mp;存储前缀和以及对应前缀和出现次数。每次在哈希表mp中查找前缀和presum-k是否存在,如果存在,则令ans+=mp[presum];其实我也不是很清楚这一步到底为什么能转换成这样,但是通过力扣题解评论区大佬给的例子nums=[1,2,3,0,6],k=6来看,好像是将原本的前缀和的形式进行了转移。
需要知识点
1. unordered_map<int,int> mp;//声明哈希表形式
2. vector& nums;int len=nums.size();//vector数组获取数组长度
3. for(auto& i: nums)//通过自动变量i对nums进行遍历,不然的话,要通过迭代器vector::iterator iter=nums.begin();的形式来处理了
题解(其实跟官方的没啥区别。。)
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> mp;
mp[0]=1;
int ans=0,pre=0,sum=0,len=nums.size();
for(auto& i:nums)
{
sum+=i;
if(mp.find(sum-k)!=mp.end())
{
ans+=mp[sum-k];
}
mp[sum]++;
}
return ans;
}
};