Leetcode523. 连续的子数组和

链接: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;
    }
};
posted @ 2019-12-13 20:43  Litn  阅读(...)  评论(...编辑  收藏