题目难度⭐️⭐️
原题:
问题描述
小M是一个五年级的小学生,今天他学习了整除的知识,想通过一些练习来巩固自己的理解。他写下了一个长度为 n 的正整数序列 a_0, a_1, ..., a_{n-1},然后想知道有多少个连续子序列的和能够被一个给定的正整数 b 整除。你能帮小M解决这个问题吗?
测试样例
样例1:
输入:
n = 3,b = 3,sequence = [1, 2, 3]
输出:3
样例2:
输入:
n = 4,b = 5,sequence = [5, 10, 15, 20]
输出:10
样例3:
输入:
n = 5,b = 2,sequence = [1, 2, 3, 4, 5]
输出:6
问题分析:
去除题目背景就变成:
给定一个长度为 n 的正整数序列,以及一个正整数 b,我们需要找出所有连续子序列的和能够被 b 整除的情况。
关键概念:
-
连续子序列:指的是序列中相邻的元素组成的子集。例如,序列
[1, 2, 3, 4]的连续子序列有[1],[2],[3],[4],[1, 2],[2, 3],[3, 4],[1, 2, 3],[2, 3, 4],[1, 2, 3, 4]等。 -
前缀和:这是一个有效的技巧,可以用来快速计算任意子序列的和。前缀和数组
prefix_sum的定义是prefix_sum[i]表示从序列开始到索引i-1的和。可以通过prefix_sum[j] - prefix_sum[i-1]计算子序列sequence[i]到sequence[j]的和。 -
模运算:我们关心的是子序列的和是否能被
b整除。因此,实际上只需要关注前缀和的模b的值。若两次前缀和的模值相同,则它们之间的子序列和能够被b整除。
算法步骤:
-
计算前缀和:创建一个数组
prefix_sum来存储前缀和。 -
利用哈希表:使用哈希表(字典)记录每个前缀和模
b的值出现的次数。当当前前缀和模值在哈希表中已存在时,说明可以找到一个或多个之前的前缀和,使得两者之间的子数组和能够被b整除。 -
更新计数:每次遇到新的前缀和模值时,更新计数器,并将当前模值记录到哈希表中。
示例分析:
序列 [1, 2, 3],对 b=3,连续子序列的和包括:
[1]和 1 不能整除 3。[2]和 2 不能整除 3。[3]和 3 能整除 3。[1, 2]和 3 能整除 3。[2, 3]和 5 不能整除 3。[1, 2, 3]和 6 能整除 3。
所以总共有 3 个子序列的和能够被 3 整除。
代码:
def solution(n, b, sequence):
count = 0
# 创建一个前缀和数组
prefix_sum = [0] * (n + 1)
for i in range(1, n + 1):
prefix_sum[i] = prefix_sum[i - 1] + sequence[i - 1]
# 使用字典来记录前缀和对 b 取模的结果的频率
mod_count = {}
for i in range(n + 1):
mod_value = prefix_sum[i] % b
if mod_value < 0: # 确保模值为正
mod_value += b
# 如果 mod_value 已经存在于字典中,说明可以找到以当前索引为结尾的
# 子数组,其和能被 b 整除
if mod_value in mod_count:
count += mod_count[mod_value]
# 记录当前前缀和的 mod 值
if mod_value in mod_count:
mod_count[mod_value] += 1
else:
mod_count[mod_value] = 1
return count
if __name__ == "__main__":
# You can add more test cases here
sequence = [1, 2, 3]
print(solution(3, 3, sequence) == 3)