链接:https://leetcode-cn.com/problems/continuous-subarray-sum
给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。
示例 1:
输入: [23,2,4,6,7], k = 6
输出: True
解释: [2,4] 是一个大小为 2 的子数组,并且和为 6。
示例 2:
输入: [23,2,6,4,7], k = 6
输出: True
解释: [23,2,6,4,7]是大小为 5 的子数组,并且和为 42。
说明:
数组的长度不会超过10,000。
你可以认为所有数字总和在 32 位有符号整数范围内。
解1:优化的暴力
先预处理前缀和,然后暴力枚举所有可能的区间下标 [ i, j ],然后由两个前缀和相减得到区间和,再判断是否满足 n%k==sum。时间复杂度O(n2)
需要注意的是 sum=0 和 k=0 的情况,搞不好就除零异常了。
class Solution { public: bool checkSubarraySum(vector<int>& nums, int k) { int n=nums.size(); vector<int> pre_sum(n+1); for(int i=1;i<=n;i++){ pre_sum[i]+=pre_sum[i-1]+nums[i-1]; } for(int i=0;i<n-1;i++){ for(int j=i+1;j<n;j++){ int sum=pre_sum[j+1]-pre_sum[i]; if(sum==0 ||(k && sum%k==0))return true; } } return false; } };
解2:哈希表----引自力扣题解
在这种方法中,我们使用 HashMap 来保存到第 i 个元素为止的累积和,但我们对这个前缀和除以 k 取余数。原因如下:
我们遍历一遍给定的数组,记录到当前位置为止的 sum%k 。一旦我们找到新的 sum%k的值(即在 HashMap 中没有这个值),我们就往 HashMap 中插入一条记录 (sum%k, i)
现在,假设第 i 个位置的 sum%k 的值为 rem 。如果以 i 为左端点的任何子数组的和是 k 的倍数,比方说这个位置为 j ,那么 HashMap 中第 j 个元素保存的值为 (rem+n∗k)%k ,其中 n 是某个大于 0 的整数。我们会发现 (rem+n∗k)%k=rem ,也就是跟第 i 个元素保存到 HashMap 中的值相同。
基于这一观察,我们得出结论:无论何时,只要 sum%k 的值已经被放入 HashMap 中了,代表着有两个索引 i 和 j ,它们之间元素的和是 k 的整数倍。因此,只要 HashMap 中有相同的 sum%k ,我们就可以直接返回 True 。
class Solution { public: bool checkSubarraySum(vector<int>& nums, int k) { map<int,int> mp; int presum=0; mp[0]=-1; //? for(int i=0;i<nums.size();i++){ presum+=nums[i]; // if(k!=0) presum%=k; if(1 == mp.count(presum)){ if(i-mp[presum]>1) // return true; } else mp[presum]=i; } return false; } };
原文地址:https://www.cnblogs.com/reflecter/p/12037023.html