题目内容
小M是一个五年级的小学生,今天他学习了整除的知识,想通过一些练习来巩固自己的理解。他写下了一个长度为 n 的正整数序列 a_0, a_1, ..., a_{n-1},然后想知道有多少个连续子序列的和能够被一个给定的正整数 b 整除。你能帮小M解决这个问题吗?
基础知识
前缀和(Prefix Sum)
前缀和是一种常用的数组处理技术,用于快速计算数组中任意子数组的和。具体来说,前缀和数组 prefix_sum 的第 i 个元素表示原数组从开始到第 i 个元素的和。
定义
给定一个数组 a,其前缀和数组 prefix_sum 定义如下:
prefix_sum[0] = a[0]prefix_sum[i] = prefix_sum[i-1] + a[i](对于i > 0)
用途
通过前缀和数组,我们可以快速计算任意子数组的和。例如,子数组 a[i:j] 的和可以通过以下公式计算:
sum(a[i:j]) = prefix_sum[j] - prefix_sum[i-1](对于i > 0)sum(a[0:j]) = prefix_sum[j](对于i = 0)
哈希表(Hash Table)
哈希表是一种数据结构,用于存储键值对,并提供快速的查找、插入和删除操作。哈希表通过哈希函数将键映射到一个索引,从而实现高效的访问。
定义
哈希表是一种基于哈希函数的数据结构,它将键映射到一个数组的索引,从而实现快速的查找、插入和删除操作。
用途
哈希表常用于需要快速查找和计数的场景。例如,在本题中,我们使用哈希表来记录前缀和的余数出现的次数。
题目分析
解题思路
- 前缀和:我们可以计算序列的前缀和数组
prefix_sum,其中prefix_sum[i]表示从序列的开始到第i个元素的和。 - 哈希表:我们可以使用一个哈希表来记录前缀和的余数出现的次数。具体来说,如果
prefix_sum[i] % b == k,那么我们就记录余数k出现的次数。 - 计算结果:对于每个前缀和
prefix_sum[i],我们查找哈希表中余数为(prefix_sum[i] % b)的次数,并累加到结果中。
具体例子
假设我们有以下输入:
n = 5b = 2sequence = [1, 2, 3, 4, 5]
步骤1:初始化
prefix_sum = 0remainder_count = {0: 1}(初始化余数为0的次数为1)result = 0
步骤2:遍历序列
-
第一个元素
1:prefix_sum = 0 + 1 = 1remainder = 1 % 2 = 1remainder_count中没有余数1,所以不累加结果- 更新
remainder_count:remainder_count = {0: 1, 1: 1}
-
第二个元素
2:prefix_sum = 1 + 2 = 3remainder = 3 % 2 = 1remainder_count中有余数1,累加结果:result = 0 + 1 = 1- 更新
remainder_count:remainder_count = {0: 1, 1: 2}
-
第三个元素
3:prefix_sum = 3 + 3 = 6remainder = 6 % 2 = 0remainder_count中有余数0,累加结果:result = 1 + 1 = 2- 更新
remainder_count:remainder_count = {0: 2, 1: 2}
-
第四个元素
4:prefix_sum = 6 + 4 = 10remainder = 10 % 2 = 0remainder_count中有余数0,累加结果:result = 2 + 2 = 4- 更新
remainder_count:remainder_count = {0: 3, 1: 2}
-
第五个元素
5:prefix_sum = 10 + 5 = 15remainder = 15 % 2 = 1remainder_count中有余数1,累加结果:result = 4 + 2 = 6- 更新
remainder_count:remainder_count = {0: 3, 1: 3}
最终结果
result = 6
代码实现
以下是完整的代码实现:
def solution(n, b, sequence):
# 初始化前缀和数组和哈希表
prefix_sum = 0
remainder_count = {0: 1} # 初始化余数为0的次数为1
result = 0
for num in sequence:
# 更新前缀和
prefix_sum += num
# 计算当前前缀和的余数
remainder = prefix_sum % b
# 如果哈希表中存在相同的余数,累加结果
if remainder in remainder_count:
result += remainder_count[remainder]
# 更新哈希表中余数的次数
if remainder in remainder_count:
remainder_count[remainder] += 1
else:
remainder_count[remainder] = 1
return result
if __name__ == "__main__":
sequence = [1, 2, 3]
print(solution(3, 3, sequence) == 3)
详细解释
- 前缀和:前缀和数组
prefix_sum用于存储从序列开始到当前元素的和。通过计算前缀和,我们可以快速计算任意子序列的和。 - 哈希表:哈希表
remainder_count用于记录每个余数出现的次数。初始时,余数为0的次数为1,因为空序列的和为0,且0可以被任何数整除。 - 遍历序列:对于每个元素,我们更新前缀和
prefix_sum,并计算当前前缀和的余数remainder。 - 查找和更新:如果
remainder已经在remainder_count中,说明存在之前的前缀和与当前前缀和的差值可以被b整除,因此累加结果。然后更新remainder_count中remainder的次数。
时间复杂度
- 时间复杂度:
O(n),其中n是序列的长度。我们只需要遍历一次序列,并在每次遍历时进行常数时间的操作。 - 空间复杂度:
O(b),其中b是给定的整数。哈希表remainder_count最多存储b个不同的余数。
总结
通过使用前缀和和哈希表,我们可以高效地计算出所有满足条件的连续子序列的数量。