问题描述:
给定一个整数数组 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
问题分析:
这个题目应该也是18年一个公司的秋招题目,特别的巧妙的方法自己也没有想到。都笨方法,看了官方的解答,才恍然大悟,感觉更像是一个数学题目。现在总结一下:
(1)可以设 P[i] = (A[0] + A[1] + ... + A[i])%K
,现在仔细想一下,如果P[i] = P[j]
,且j>i
。是不是可以得出来子序列A[i, j] 是可以被K整除的。想想为什么?因为这两个子序列余数相同,很显然(A[0] + A[1] + ... + A[i]+... + A[j]) - (A[0] + A[1] + ... + A[i]) = A[i, j]
的差的余数一定为0
,也就是整除了。
(2)现在问题就简化了很多:
- 首先,要求出这些余数来,也就是
P[i]
。 - 然后把相同的余数,它们的个数统计出来。
- 例如,某一个余数的个数为
5
,是不是可以的出来,5 *(5-1)//2 = 10
(任意两两配对)。 - 依次类推,把所有的结果求出,然后求和即可。
Python3实现:
方法一(官方版):
import collections
class Solution():
def subarraysDivByK(self, A, K):
P = [0]
for x in A:
P.append((P[-1] + x) % K)
count = collections.Counter(P)
return sum(v*(v-1)/2 for v in count.values())
方法二:
# @Time :2018/06/17
# @Author :LiuYinxing
# 数学
from collections import defaultdict
class Solution:
def subarraysDivByK(self, a, k):
counts = defaultdict(int) # 用于对各个余数进行计数
counts[0] = 1 # 因为 0 本身就是一个,所以要先初始化
cur_sum = 0 # 记录当前列表的和
ans = 0 # 记录可被整数的子序列数
for num in a:
cur_sum += num
mod = cur_sum % k # 取余
ans += counts[mod] # 累计
counts[mod] += 1 # 计数
return ans
if __name__ == '__main__':
solu = Solution()
A, K = [4, 5, 0, -2, -3, 1], 5
print(solu.subarraysDivByK(A, K))
更喜欢第二种方法
声明: 总结学习,有问题或不当之处,可以批评指正哦,谢谢。