[NEO解题报告]《Leetcode》523 -- 连续的子数组和

1. 题目信息

1.1 题目描述

题目链接: 523. 连续的子数组和

在这里插入图片描述

1.2 测试用例

示例 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 。 
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。
  • 示例 3:
输入:nums = [23,2,6,4,7], k = 13
输出:false
  • 提示:
1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= sum(nums[i]) <= 231 - 1
1 <= k <= 231 - 1

2. 题目分析

2.1 前缀和+哈希存余数

根据题意, 很容易想到前缀和来减少循环次数;

  • 直观做法, 得到前缀和之后, 2层循环遍历开始和结束位置, 对形成的区间, 用 (preSum[j] - preSum[i] % k) 进行判定, O ( N 2 ) O(N^2) O(N2), 还是会有部分用例不过;
  • 哈希优化, 存储前缀和同k的余数即可, 找到相同余数的时候, 说明中间的求和结果为k的倍数;

证明如下:

  • 假设可以找到两个下标i和j满足题意, 令:
    • p r e S u m [ i ] = a k + b preSum[i] = ak + b preSum[i]=ak+b
    • p r e S u m [ j ] = c k + d = p r e S u m [ i ] + e k preSum[j] = ck + d = preSum[i] + ek preSum[j]=ck+d=preSum[i]+ek ;
  • 则: p r e S u m [ j ] = c k + d = p r e S u m [ i ] + e k = ( a k + b ) + e k preSum[j] = ck + d = preSum[i] + ek = (ak + b) + ek preSum[j]=ck+d=preSum[i]+ek=(ak+b)+ek
    • => c k + d = ( a k + b ) + e k ck + d = (ak + b) + ek ck+d=(ak+b)+ek
    • => ( c − a − e ) k = d − b (c - a - e)k = d - b (cae)k=db任意k 都成立;
  • 所以 => c − a − e = 0 且 d − b = 0 c - a - e = 0 且 d - b = 0 cae=0db=0 ; 即 余数 d == 余数 b;
  • 所以对满足要求的ij来说, 求余后, 余数应该相同;

3. 代码详情

3.1 C++

3.1.1 前缀和+哈希存余数

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        // 执行用时:212 ms, 在所有 C++ 提交中击败了33.29%的用户
        // 内存消耗:94.3 MB, 在所有 C++ 提交中击败了25.66%的用户
        int n = nums.size();
        if (n < 2) {
            return false;
        }

        unordered_map<int, int> prevRemainderIndex;
        prevRemainderIndex[0] = -1; // 开始时, 累计和为0, 余数也为0, 认为是k的倍数

        int remainder = 0;
        for (int i = 0; i < n; i++) {
            remainder = (remainder + nums[i]) % k; // 累加余数即可
            if (prevRemainderIndex.count(remainder)) {
                if (i - prevRemainderIndex[remainder] >= 2) {
                    return true;
                }
            } else {
                // 存储第一次出现的下标即可, 贪心思想, 更容易满足 距离大于2的要求;
                prevRemainderIndex[remainder] = i;
            }
        }

        return false;
    }
};

3.2 Python

3.2.1 前缀和+哈希存余数

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

        return False

4. 系列文章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逸云沙鸥のIHave@Dream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值