题目
给定一个整数数组nums和一个整数k,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
- 子数组大小至少为2,且
- 子数组元素总和为k的倍数。
如果存在,返回true;否则,返回false。
如果存在一个整数n,令整数n符合
x
=
n
∗
k
x=n*k
x=n∗k,则称x是k的一个倍数。
前缀和 + 哈希表 + 同余定理
最直接的思路是遍历数组
n
u
m
s
nums
nums中每个大小至少为2的子数组并计算每个子数组的元素和,判断是否存在一个子数组的元素和为
k
k
k的倍数。当数组
n
u
m
s
nums
nums的长度为
m
m
m时,需要用
O
(
m
2
)
O(m^2)
O(m2)的时间来遍历全部子数组,然后,对于每个子数组,需要
O
(
m
)
O(m)
O(m)的时间计算元素和,因此此时的时间复杂度是
O
(
m
3
)
O(m^3)
O(m3)。
如果提前计算出数组
n
u
m
s
nums
nums的前缀和数组,则对于任意一个子数组,都可以在
O
(
1
)
O(1)
O(1)的时间内得到其元素和。用
p
r
e
f
i
x
S
u
m
s
[
i
]
prefixSums[i]
prefixSums[i]表示数组
n
u
m
s
nums
nums从下标0到下标
i
i
i的前缀和,则
n
u
m
s
nums
nums从下标
p
+
1
p+1
p+1到下标
q
q
q(
p
<
q
p<q
p<q)的子数组长度为
q
−
p
q-p
q−p,该子数组的元素和为
p
r
e
f
i
x
S
u
m
s
[
q
]
−
p
r
e
f
i
x
S
u
m
s
[
p
]
prefixSums[q] - prefixSums[p]
prefixSums[q]−prefixSums[p]。
如果
p
r
e
f
i
x
S
u
m
s
[
q
]
−
p
r
e
f
i
x
S
u
m
s
[
p
]
prefixSums[q]-prefixSums[p]
prefixSums[q]−prefixSums[p]为
k
k
k的倍数,且
p
−
q
≥
2
p-q\geq 2
p−q≥2,则上述子数组即满足大小至少为2且元素和为
k
k
k的倍数。
当
p
r
e
f
i
x
S
u
m
s
[
q
]
−
p
r
e
f
i
x
S
u
m
s
[
p
]
prefixSums[q]-prefixSums[p]
prefixSums[q]−prefixSums[p]为
k
k
k的倍数时,
p
r
e
f
i
x
S
u
m
s
[
p
]
prefixSums[p]
prefixSums[p]和
p
r
e
f
i
x
S
u
m
s
[
q
]
prefixSums[q]
prefixSums[q]除以
k
k
k的余数相同。因此只需要计算每个下标对应的前缀和除以
k
k
k的余数即可,使用哈希表存储每个余数第一次出现的下标。
固定空的前缀的结束下标为-1,由于空的前缀的元素和为0,因此在哈希表中存入键值对(-1,0)。对于
0
≤
i
<
m
0\leq i < m
0≤i<m,从小到大依次遍历每个
i
i
i,计算每个下标对应的前缀和除以
k
k
k的余数,并维护哈希表:
- 如果当前余数在哈希表中已存在,则取出该余数在哈希表中对应的下标 p r e I n d e x preIndex preIndex, n u m s nums nums从下标 p r e I n d e x + 1 preIndex+1 preIndex+1到下标 i i i的子数组长度为 i − p r e I n d e x i-preIndex i−preIndex,该子数组的元素和为 k k k的倍数,如果 i − p r e I n d e x ≥ 2 i-preIndex\geq 2 i−preIndex≥2,则找到了一个大小至少为2且元素和为 k k k的倍数的子数组。
- 如果当前余数不在哈希表的中,则将当前余数和当前下标 i i i的键值存入哈希表中。
由于哈希表存储的是每个余数第一次出现的下标,因此当遇到重复的余数时,根据当前下标和哈希表中存储的下标计算得到子数组长度是以当前下标结尾的子数组中满足元素和为 k k k的倍数的子数组长度中的最大值。只要最大长度至少为2,即存在符合要求的子数组。