leetcode:523. 连续的子数组和

题目

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

  • 子数组大小 至少为 2 ,且
  • 子数组元素总和为 k 的倍数。

如果存在,返回 true ;否则,返回 false
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 xk 的一个倍数。0 始终视为 k 的一个倍数。

示例:

  1. 输入:nums = [23,2,4,6,7], k = 6
  2. 输出:true
  3. 解释:[2,4] 是一个大小为 2 的子数组,并且和为 6
输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。 
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。
输入:nums = [23,2,6,4,7], k = 13
输出:false

解答 & 代码

前缀和取余 + 哈希表
思想:如果 preSum[i] % k == preSum[j] % k,那么 preSum[j] - preSum[i] % k == 0

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        int len = nums.size();
        int sum = 0;    // 前缀和对 k 取余
        // 哈希表,存储 <前缀和除以 k 的余数,第一次出现的下标>
        unordered_map<int, int> sumMap;
        sumMap[0] = -1;    // 注意:初始化存入空串的情况,前缀和余数为 0,下标 -1
        // 遍历数组
        for(int i = 0; i < len; ++i)
        {
            sum += nums[i];    // 计算当前的前缀和
            sum %= k;        // 并对 k 取余
            // 如果这个前缀和区域之前出现过(假设位置 j),那么 nums[j+1,...,i] 部分的和是 k 的倍数
            if(sumMap.find(sum) != sumMap.end())
            {
                // 如果区间长度 >=2 ,则找到了满足要求的连续子数组,返回 true
                if( i - sumMap[sum] >= 2)    
                    return true;
            }
            else
                sumMap[sum] = i;
        }
        return false;
    }
};

复杂度分析:设数组 nums 长为 n,整数 k

  • 时间复杂度 O(n):
  • 空间复杂度 O(min(n, k)):哈希表存储前缀和除以 k 的余数,及其第一次出现的下标

执行结果:

执行结果:通过

执行用时:144 ms, 在所有 C++ 提交中击败了 72.75% 的用户
内存消耗:94.2 MB, 在所有 C++ 提交中击败了 72.25% 的用户