leetcode974. 和可被 K 整除的子数组
给定一个整数数组 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
方法:前缀和+哈希表
思路:
本题与leetcode560题类似,
Andy Liu:leetcode560. 和为K的子数组(前缀和+哈希表)zhuanlan.zhihu.com只不过一个是求和为k的子数组,一个是求和可被k整除的子数组。方法是一样的。
首先我们考虑暴力法,维护dp数组表示前缀和,那么可知,i-j这个子数组的和为:dp[j] - dp[i-1],然后双层循环遍历,找到符合的情况计数即可,这样的时间复杂度是O(N^2),我们查看数据规模,发现肯定会超时。
我们同样使用哈希表来完成这个过程,首先使用pre变量来遍历数组存储到目前为止的前缀和,建立一个字典dp,它的键为余数,值为出现这个余数的个数。
dp[1]就表示为遍历到目前为止,前面出现的前缀和mod K得到的余数为1的位置数量。如果我们在遍历计算前缀和的时候,遇到了前缀和的余数为1,那么我们对这个位置,减去之前也出现余数为1的位置,这两个位置区间构成的子数组肯定可以被K整除,所以我们就对答案ans += dp[1],然后对dp[1] += 1更新。
我们还需要考虑到一种情况,如果一个数组,不是两个位置相减得到的子数组,而是从头开始到目前为止的子数组就满足条件,这个也需要考虑。那么容易知道,从头开始到目前位置的pre对于K的余数为0,也可以知道,每当pre%K == 0的时候,我们除了考虑与之前也为0的位置相减,还需要考虑一个从头开始的子数组也满足。所以我们只需要初始化对dp[0] = 1 即可。
代码:
class Solution:
def subarraysDivByK(self, A: List[int], K: int) -> int:
pre = 0
dp = dict() #存放前缀和%K为0,1,2,3...的位置个数
#初始化一个余数为0的子数组为1,这种情况是考虑到从头开始的到此处满足条件的数组的情况
dp[0] = 1
ans = 0
temp = 0
n = len(A)
for i in range(n):#遍历数组
pre += A[i] #计算到此为止前缀和
temp = pre % K #计算此处前缀和对于K的余数
if temp in dp:
ans += dp[temp] #dp[temp]表示这个余数temp的前面的前缀和出现的个数,现在的位置与之前同样是这个余数的位置相减得到的数组即为所求。
dp[temp] += 1
else: #dp中不存在这个余数的话,添加,数量记为1
dp[temp] = 1
return ans
结果:
![b6fda09c0bbab5f2f8e15b8b2cb3ede7.png](https://i-blog.csdnimg.cn/blog_migrate/6cf2b15d4ff663094cf2171f35e40fe0.png)