在问 · 算法题解

523. 连续的子数组和

题目描述

输入整数数组 nums 和整数 k。

判断 nums 中是否存在一段连续的子数组,同时满足两点:长度不少于 2;元素之和是 k 的整数倍。这里 0 也算作 k 的倍数。

存在则返回 true,否则返回 false。

示例 1
输入:nums = [23,2,4,6,7], k = 6
输出:true

解释:[2,4] 的和为 6,是 6 的倍数。

示例 2
输入:nums = [23,2,6,4,7], k = 6
输出:true

解释:整个数组的和为 42,等于 7 * 6。

示例 3
输入:nums = [23,2,6,4,7], k = 13
输出:false

解释:找不到长度至少为 2 且和为 13 的倍数的连续段。

数据范围

  • 1 <= nums.length <= 10^5
  • 0 <= nums[i] <= 10^9
  • 0 <= sum(nums[i]) <= 2^31 - 1
  • 1 <= k <= 2^31 - 1

解题思路

子数组 nums[j+1..i] 的和等于前缀和之差 P[i] - P[j]。它是 k 的倍数,当且仅当 P[i] 和 P[j] 除以 k 的余数相同。所以用哈希表记录每个余数第一次出现的下标(空前缀的余数 0 记在下标 -1)。再次遇到同一余数时,只要距离至少 2 就找到了。只保留最早的下标,是为了让子数组尽可能长。

为什么正确

first[r] 是已扫描前缀中余数为 r 的最小下标;若当前余数 s 已在表中,则以 i 结尾的最长合法子数组是 nums[first[s]+1..i]。

复杂度分析

时间 O(n),空间 O(min(n, k))。

易错点

忘记放入 {0: -1},会漏掉从下标 0 开始的子数组;遇到重复余数时用新下标覆盖旧下标,会让 [5,0,0] 这类数据在 k 任意时错失长度为 2 的解。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def checkSubarraySum(self, nums: List[int], k: int) -> bool:
        first = {0: -1}
        s = 0
        for i, x in enumerate(nums):
            s = (s + x) % k
            if s in first:
                if i - first[s] >= 2:
                    return True
            else:
                first[s] = i
        return False

载入编辑器,自己跑一遍 →

哈希表 专题的其他题目

查看 哈希表 全部题目 →