您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

[20210602]LeetCode每日一题 - 523. 连续的子数组和

题目描述【中等】

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

子数组大小 至少为 2 ,且 子数组元素总和为 k 的倍数。 如果存在,返回 true ;否则,返回 false

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 xk 的一个倍数。

示例 1:

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6

示例 2:

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

示例 3:

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

提示:

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

题目地址:地址


初步分析

本题涉及到一个算法:前缀和

  • 何为前缀和
    前缀和是为了快速求解类似于此类需要做 连续加法 的题而使用的数学工具。简单来说,就是一个新的、长度和原数组相同的数组 sumsum 的第 i 项是原数组 从 0 ~ i 的和。

  • 举个例子
    例:需要求得 [a, b] 区间范围的和,则可以用 sum[b] - sum[a - 1] 求得。如下:
    origin = [1, 2, 3, 4, 5]
    sum = [1, 3, 6, 10, 15]

    如果我要快速求得 origin 下标是 [2, 4] 之间的值的和,那我可以直接用 sum 数组的 sum[4] - sum[1] = 15 - 3 = 12 获得

  • 需要注意的是

    • 如果区间左侧下标为 0 时,注意不要越界(可以在初始化 sum 时,就预留第一位为 0 来避免越界,当然具体求解 [a, b] 区间时,也会变为 sum[b + 1] - sum[a]
    • 在实际解题的时候,可能不需要真的 new 出这个 sum 数组,而是用一个变量来累计循环数组的每项和而计算。就像今天的这道题一样。具体如何求解还是需要看题目的需求

如果需要做一个经典的题来练习这个算法,可以参考这个:560. 和为K的子数组


正式分析

我们继续使用上面分析使用的各项定义来具体分析此题

示例一 为例: nums = [23, 2, 4, 6, 7], k = 6
为了符合题目要求: 令整数 x 符合 x = n * k ,那么必须是 nums 前缀和可以被 k 整除

那么我们可以由 求前缀和 改为 求前缀余数, 如果前缀余数为 0,则代表在当前这个区间中,存在一个子区间满足 令整数 x 符合 x = n * k

而我们可以定义一个变量存放每次与 6 取余得到的余数,然后与循环值相加,如果再次得到的余数为 0 ,则代表满足了 令整数 x 符合 x = n * k 条件:

  1. 23 % 6 = 5 ,即 [0, 1] 之间不能满足条件
  2. (5 + 4) % 6 = 3,即 [0, 2] 之间不能满足条件
  3. (3 + 6) % 6 = 0,此时 [0, 3] 之间存在某子区间满足条件,返回 true

我们观察上述过程,忽略了一个问题,就是假如开始的值本身就符合条件,例如 nums = [6, 3, 5, 9, 2], k = 6,如果以上述方法计算,则会返回 true,实际上却不满足题目要求的另一个条件: 子数组大小 至少为 2。因此我们需要将每次取余后的值的 下标 记录并获取,然后判断 前一个取余后值得下标当前值下标 之间是否 >= 2 。所以我们可以使用 哈希表 来存储此对应关系。


具体代码

function checkSubarraySum(nums: number[], k: number): boolean {
    if (nums.length < 2) return false

    const map = new Map()
    map.set(0, -1)

    const length = nums.length
    let remain = 0

    for (let i = 0; i < length; i++) {
        remain = (remain + nums[i]) % k

        if (map.has(remain)) {
            const prevIndex = map.get(remain)

            if (i - prevIndex >= 2) return true

        } else {
            map.set(remain, i)
        }
    }

    return false
};

TS AC,执行用时:128ms, 在所有 TypeScript 提交中击败了 33.33% 的用户

TS AC中击败 100% 用户的提交,目前执行下来用时 180ms,估计力扣增加了测试用例导致更简单的算法耗时比之前提交的复杂的算法耗时要久,这对后期用户提交并不友好


问题升级

本题用到了一位数组的前缀和,那么自然也有二维数组前缀和
可以参考此题:304. 二维区域和检索 - 矩阵不可变

各大公司对前缀和的考察还是比较多的


最后

如果我在哪里写的有问题,欢迎指出,共同进步,谢谢阅读~


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进