题目
代码
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
}
};
方法一:前缀和+哈希表
分析
朴素思路:当数组 n u m s nums nums 的长度为 n n n 时,使用 O ( n 2 ) O(n^2) O(n2)的时间遍历遍历数组 n u m s nums nums 的全部子数组,对于每个子数组需要 O ( n ) O(n) O(n) 的时间计算元素和,因此时间复杂度是 O ( n 3 ) O(n^3) O(n3)。
同余定理:如果两个整数 a a a、 b b b 满足 a − b a-b a−b 能被 k k k 整除,那么 a a a、 b b b 对 k k k 同余。因此
因此我们首先计算出数组
n
u
m
s
nums
nums 的前缀和数组
s
u
m
s
sums
sums:
s
u
m
s
[
i
]
=
∑
i
=
0
n
n
u
m
s
[
i
]
{sums[i]} = \sum^{n}_{i = 0}{nums[i]}
sums[i]=i=0∑nnums[i] 假设
0
≤
j
<
i
<
n
0 \leq j < i < n
0≤j<i<n, 对于从下标
j
j
j 到下标
i
i
i 的子数组的元素和为
s
u
m
s
[
i
]
−
s
u
m
s
[
j
]
sums[i] - sums[j]
sums[i]−sums[j]。根据同余定理从下标
j
j
j 到下标
i
i
i 的子数组的元素和为
k
k
k 的倍数时,
s
u
m
s
[
i
]
sums[i]
sums[i] 和
s
u
m
s
[
j
]
sums[j]
sums[j] 余数相同。因此,可以将题目表示为:
- 存在 j j j 和 i i i 使得 s u m s [ i ] sums[i] sums[i] 和 s u m s [ j ] sums[j] sums[j] 余数相同且 i − j ≥ 2 i - j \geq 2 i−j≥2时,返回 t r u e true true;
- 存在 j j j 和 i i i 使得 s u m s [ i ] sums[i] sums[i] 和 s u m s [ j ] sums[j] sums[j] 余数相同且 i − j < 2 i - j < 2 i−j<2时,不做操作继续运行;
在此基础上,我们可以使用哈希表记录任意余数值第一次出现的位置,并且使用一个动态更新数值 c u r cur cur 来替代前缀和数组 s u m s sums sums。因此,可以将题目进行简化:
- 对于第 i i i 个当前余数: c u r = ( c u r + n u m s [ i ] ) % k cur = (cur + nums[i])\%k cur=(cur+nums[i])%k;
- 如果当前余数在哈希表中不存在,则将当前余数和当前下标 i i i 的以及当前余数 c u r cur cur 的键值对存入哈希表中。
- 如果当前余数在哈希表中已经存在,则取出该余数在哈希表中对应的下标 j j j,当 i − j ≥ 2 i - j \geq 2 i−j≥2时,返回 t r u e true true;
- 如果当前余数在哈希表中已经存在且 i − j < 2 i - j < 2 i−j<2时,不做操作继续运行;
代码
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
int cur = 0, len = nums.size();
unordered_map<int, int> hash;
hash[0] = -1;
for(int i = 0; i < len; ++i) {
cur = (cur + nums[i]) % k;
if(hash.count(cur) && i - hash[cur] >= 2) {
return true;
} else if(hash.count(cur)) {
continue;
} else {
hash[cur] = i;
}
}
return false;
}
};
要点
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 n u m s nums nums 的长度。需要遍历数组一次。哈希表可以在 O ( 1 ) O(1) O(1) 的时间进行查找。
- 空间复杂度: O ( m i n ( n , k ) ) O(min(n,k)) O(min(n,k))。空间复杂度主要取决于哈希表,哈希表中存储每个余数第一次出现的下标,最多有 m i n ( n , k ) min(n,k) min(n,k) 个余数。