文章目录
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 (c−a−e)k=d−b 对
任意k
都成立;- 所以 => c − a − e = 0 且 d − b = 0 c - a - e = 0 且 d - b = 0 c−a−e=0且d−b=0 ; 即
余数 d == 余数 b
;- 所以对满足要求的
i
和j
来说, 求余后, 余数应该相同;
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