题目描述

:::fold-green 相关原题

LeetCode 原题: ✅ 523. 连续的子数组和

:::

给你一个 32 位整数数组 nums 和一个正整数 k,请返回元素和能够被 k 整除的最短非空连续子数组的长度。

数组中允许有负数,长度为 1 的子数组也合法。如果不存在符合条件的子数组,返回 -1。

示例 1:

输入: nums = [2,3,1,2], k = 3
输出: 1
解释: 单元素子数组 [3] 的和已能被 3 整除。

示例 2:

输入: nums = [1,1,1], k = 3
输出: 3
解释: 只有整个数组的和能被 3 整除。

示例 3:

输入: nums = [1], k = 2
输出: -1
解释: 没有满足要求的非空连续子数组。

提示:

  • 数组元素为 32 位整数,允许负数。
  • k 为正整数。
  • 子数组必须非空,长度 1 也合法。

题意分析

子数组和等于右侧前缀和减去左侧前缀和,能被 k 整除等价于两个前缀和模 k 相等。数组允许负数,不能用窗口和单调增减的假设,前缀同余关系则始终成立。

解法:前缀余数配对最近位置

核心思路

[!blue]

扫描到下标 i 时,只需知道当前前缀余数上一次出现的位置 last,合法子数组长度就是 i - last。固定右端点时,左端越近长度越短,所以每个余数只保留最近位置,更早位置不会产生更优答案。

初始化余数 0 对应位置 -1,覆盖从下标 0 开始的合法子数组。每步先用旧位置计算长度,再覆盖为当前位置,避免把空区间误算成长度 0;Java 的 put 返回被覆盖的旧值,作用相同。

将负数余数归一化到 [0,k-1],使数学上同余的前缀拥有相同键。累计时仅保存余数,不必累加完整前缀和;若始终没有配对则返回 -1。

解题步骤

  1. 预置余数 0 的位置为 -1,表示空前缀。
  2. 逐个累加并归一化余数,取得该余数上次出现的位置。
  3. 有旧位置就更新长度差,再让当前位置成为最新位置;无候选返回 -1。

代码实现

class Solution {
    public int shortestMultiple(int[] a, int k) {
        Map<Long, Integer> latest = new HashMap<>();

        latest.put(0L, -1);
        long remainder = 0;
        int best = a.length + 1;

        for (int i = 0; i < a.length; i++) {
            remainder = Math.floorMod(remainder + a[i], (long) k);
            Integer last = latest.put(remainder, i);

            if (last != null) {
                best = Math.min(best, i - last);
            }
        }

        return best <= a.length ? best : -1;
    }
}
func shortestMultiple(a []int, k int) int {
    latest := map[int64]int{0: -1}
    remainder := int64(0)
    best := len(a) + 1
    mod := int64(k)
    for i, v := range a {
        remainder = ((remainder+int64(v))%mod + mod) % mod
        if last, ok := latest[remainder]; ok {
            best = min(best, i-last)
        }
        latest[remainder] = i
    }
    if best > len(a) {
        return -1
    }
    return best
}

复杂度分析

  • 时间复杂度:期望 $O(n)$。
  • 空间复杂度:额外空间 $O(\min (n,k))$。

关键点总结

[!green]

同余关系确定合法性,保存最近或最早位置则由最短或最长目标决定,不能机械复用同一张位置表。

易错点总结

[!yellow]

初始余数0对应位置-1;负数余数需归一化。若另要求长度至少2,不能直接套用当前最近位置策略。

相似题目

题目 难度 关联与区别
523. 连续的子数组和 中等 原题要求长度至少 2 并判断存在,本题允许长度 1 且求最短,不能直接复用最早位置策略。
974. 和可被 K 整除的子数组 中等 同余前缀的判据相同;计数题存频次,最短长度题存最近下标。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/11828931
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!