题目概述
小M是一个五年级的小学生,今天他学习了整除的知识,想通过一些练习来巩固自己的理解。他写下了一个长度为 n 的正整数序列 a_0, a_1, ..., a_{n-1},然后想知道有多少个连续子序列的和能够被一个给定的正整数 b 整除。你能帮小M解决这个问题吗?
解题思路详解
在面对计算能被给定数整除的连续子序列数量的问题时,我们可以采用一种基于前缀和与哈希表(字典)的高效方法。以下是详细的解题思路:
1. 引入前缀和数组
前缀和数组是一个重要的工具,它允许我们在常数时间内计算出任意连续子序列的和。具体地,对于给定的整数序列 sequence,我们可以构造一个前缀和数组 prefix_sum,其中 prefix_sum[i] 存储了从序列开头到第 i 个位置(包含第 i 个位置)的所有元素的累加和。
2. 利用哈希表记录余数信息
为了快速判断某个子序列的和是否能被给定的数 b 整除,我们可以利用哈希表(在Python中通常使用字典)来记录前缀和模 b 的余数及其出现的次数。这样做的好处是,当我们遍历序列并计算前缀和时,可以立即查找之前是否出现过相同的余数。如果出现过,说明存在之前的某个位置到当前位置的子序列和可以被 b 整除。
3. 遍历序列并更新状态
我们遍历整数序列 sequence,同时维护前缀和数组 prefix_sum 和哈希表 remainder_count。对于每个位置 i,我们计算前缀和 prefix_sum[i],然后计算其余数 remainder = prefix_sum[i] % b。
- 如果
remainder为0,说明从序列开头到当前位置的子序列和能被b整除,我们直接将结果计数器result加1。 - 接下来,我们查找哈希表
remainder_count中是否存在相同的余数。如果存在,说明存在之前的某个位置j(j < i),使得prefix_sum[j] % b == remainder。这意味着从位置j+1到位置i的子序列和prefix_sum[i] - prefix_sum[j]能被b整除(因为两者模b的余数相同,相减后余数仍为0)。我们将这些子序列的数量(即哈希表中该余数的值)加到result中。 - 最后,我们更新哈希表
remainder_count,记录当前余数remainder出现的次数。
4. 返回最终结果
遍历完成后,result 中存储的是所有能被 b 整除的子序列的数量。我们返回该值作为最终结果。
5.代码详解
代码分块详解
函数定义与初始化
python复制代码
def solution(n, b, sequence):
# 初始化前缀和数组和字典
prefix_sum = [0] * (n + 1)
remainder_count = {}
result = 0
def solution(n, b, sequence)::定义了一个名为solution的函数,它接受三个参数:n(序列的长度),b(需要被整除的数),以及sequence(给定的整数序列)。prefix_sum = [0] * (n + 1):初始化了一个长度为n+1的前缀和数组prefix_sum,并将所有元素初始化为0。这里数组长度设置为n+1是为了方便计算,使得prefix_sum[i]表示从序列开头到第i个元素(包含)的累加和。remainder_count = {}:初始化了一个空字典remainder_count,用于存储每个前缀和模b的余数及其出现的次数。result = 0:初始化结果计数器result为0,用于存储能被b整除的子序列的数量。
计算前缀和并处理余数
python复制代码
# 计算前缀和
for i in range(1, n + 1):
prefix_sum[i] = prefix_sum[i - 1] + sequence[i - 1]
# 计算当前前缀和的余数
remainder = prefix_sum[i] % b
for i in range(1, n + 1)::遍历序列sequence,索引从1开始到n结束(因为prefix_sum和sequence的索引是对齐的,但prefix_sum的第一个元素是0,用于方便计算)。prefix_sum[i] = prefix_sum[i - 1] + sequence[i - 1]:计算当前位置的前缀和,并将其存储在prefix_sum[i]中。remainder = prefix_sum[i] % b:计算当前前缀和模b的余数,并存储在变量remainder中。
更新结果和字典
python复制代码
# 如果余数为0,说明从开头到当前位置的子序列和能被b整除
if remainder == 0:
result += 1
# 查找字典中是否存在相同的余数
if remainder in remainder_count:
result += remainder_count[remainder]
# 更新字典
if remainder in remainder_count:
remainder_count[remainder] += 1
else:
remainder_count[remainder] = 1
if remainder == 0::如果余数为0,说明从序列开头到当前位置的子序列和能被b整除,因此将result加1。if remainder in remainder_count::查找字典remainder_count中是否存在相同的余数。如果存在,说明之前已经计算过某个位置到某个更早位置之间的子序列和能被b整除,因此将这些子序列的数量(即字典中该余数的值)加到result中。if remainder in remainder_count:和else::更新字典remainder_count,记录当前余数remainder出现的次数。如果余数已经存在,则将其计数加1;如果余数不存在,则将其添加到字典中,并设置计数为1。
返回结果
python复制代码
return result
return result:返回最终的结果,即能被b整除的子序列的数量。
测试用例
python复制代码
if __name__ == "__main__":
# 测试用例
sequence = [1, 2, 3]
print(solution(3, 3, sequence) == 3) # 应该输出 True
sequence = [5, 10, 15, 20]
print(solution(4, 5, sequence) == 10) # 应该输出 True(注意:这里通常不包括空子序列,但所有子序列和都能被5整除,因此结果为10个有效子序列)
sequence = [1, 2, 3, 4, 5]
print(solution(5, 2, sequence) == 6) # 应该输出 True(有6个子序列和能被2整除)
- 这些测试用例用于验证
solution函数的正确性。每个测试用例都调用solution函数,并检查其返回值是否与预期结果相等。如果相等,则输出True;否则,输出False(但由于这里使用了==运算符进行比较,并且预期结果是直接写在代码中的,所以实际上不会看到False的输出,除非函数实现有误)。
注意事项
- 在实现过程中,需要注意前缀和数组的索引与序列索引之间的对应关系。通常,我们将前缀和数组的索引设置为从0开始,而序列索引从1开始(或者两者都从0或1开始,但要保持一致)。
- 另外,根据问题的具体要求,可能需要考虑空子序列是否计入结果。在大多数情况下,空子序列不被视为有效的子序列,因此在计算结果时通常不考虑它。然而,在某些特殊情况下(如题目明确说明或需要特殊处理),可能需要将空子序列纳入考虑范围。