523. 连续的子数组和
思路:
想到了前缀和的方法,但还是没法避免用了两重循环,然后还是超时了。
看了题解也是前缀和,但是他的前缀和求的和我想的有不同,并且还用了hash表来优化。
这里主要问题是我没能想到同余定理,什么是同余定理呢。
举个例子: 23,2,4,6,7.k=6我们知道2,4能够满足条件。那么2,4的求解为前缀和29-23,即6和2的前缀和相减的得到2+4的结果。
那么对于23和29,有23%6=29%6=5.为什么?我们知道23/6=3,29/6=4,那么有29-23=(46+5)-(36+5),所以同于定理保证了x-y=nk时,x和y变为(ab+l)形式时,x-y能把l消除掉才能使相减的结果为k的n倍。总结为,如果x、y满足x-y为k的n倍的话,那么x、y余k的结果相同。
如果想到这点,那就容易想到了,我只需要遍历一次,每次把前缀和模k的结果存入一个数据结构,我们每次存入之前都要判断数据结构中是否存入了前缀和模k结果相同的内容,已经有了我们就找到了满足k倍数的子数组。所以这个数据结构需要有高效的查找效率,所以我们采用hash表。
hash表的键为模k的结果,值为该数的下标。
我们需要先让模k=0的结果设为-1,这是一个边界条件,如果某个前缀和就为k的倍数,那么我们就算发现了一个满足条件的结果,而这个长度就等于idx-(-1),例如1,5,7.k=6,那么我们前缀和6的时候就满足条件,5的下标为1,那么1-(-1)=2这样才满足1,5的长度2。
代码:
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
int n = nums.size();
if(n<2) return false;
unordered_map<int,int> mp;
mp[0]=-1;
int presum=0;
for(int i=0;i<n;++i){
presum = (presum + nums[i])%k;
if(mp.count(presum)){
if(i-mp[presum]>=2) return true;
}
else mp[presum]=i;
}
return false;
}
};