难度:★★★☆☆
类型:数组
方法:前缀和
题目
力扣链接请移步本题传送门
更多力扣中等题的解决方案请移步力扣中等题目录
给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。
示例:
输入:A = [4,5,0,-2,-3,1], K = 5
输出:7
解释:
有 7 个子数组满足其元素之和可被 K = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
提示:
1 <= A.length <= 30000
-10000 <= A[i] <= 10000
2 <= K <= 10000
解答
面对连续子数组的和的问题,有两个亲密搭档,前缀和与哈希表,是解决这类问题的法宝。
对于数组A,它的前缀和数组指的是,数组中任意位置i的元素对应的数值为sum(A[:i]),维度与A相同。
前缀和的最大用途是,对于数组区间的和,只需要拿捏区间两端的数值就好,这是一种避免重复计算的重要方式。有了前缀和列表prefix_sum,那么sum(A[i-1:j])=prefix_sum[j]-prefix_sum[i],实现了快速计算。
还需要准备一个数学知识:
如果m%k==n%k==0,那么(m-n)%k==m%k==n%k==0,%就是取余。
这也就意味着,我们只需要记录m%k==0和n%k==0,就可以保证(m-n)%k==0,问题就更加的简化了,这也就意味着,我们的前缀和列表中所存储的数字,都是可以对k取余之后再存储的,因为这样的做法丝毫不会影响后续相减后的差值对k取余的结果。
与此同时,对于满足条件的子数组的统计,也变得更加的简化了。也就是说,我们准备一个哈希表,用来存储上述的经过对k取余后的前缀和及其对应的出现次数,每次遇到哈希表中存在相应的记录,举个例子,例如{2:3},如果再次发现i位置的前缀和%k的数值为2,则说明在i位置之前,已经存在过3个位置,他们的前缀和%k也是2,因此分别以这三个位置为起点,以i位置为终点的子数组,元素和对k取余后一定也是2(根据上面的数学规则),则说明发现了三个满足条件的数组,添加到结果中,并更新哈希表中的统计记录为{2:4}(把当前i位置处的结果也需要及时加入到哈希表计数器中,以备后续使用)。
我们编写快捷的算法,一次遍历实现前缀和的获取,前缀和的取余,结果的更新以及哈希表计数器的更新。
from collections import defaultdict
class Solution:
def subarraysDivByK(self, A, K):
res = prefix_sum = 0
counter = defaultdict(int)
counter[0] = 1
for i in range(len(A)):
prefix_sum += A[i]
prefix_sum = prefix_sum % K
res += counter[prefix_sum]
counter[prefix_sum] += 1
return res
如有疑问或建议,欢迎评论区留言~
有关更多力扣中等题的python解决方案,请移步力扣中等题解析