连续子串和的整除问题 | 豆包MarsCode AI刷题

119 阅读4分钟

题目内容

小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)

哈希表是一种数据结构,用于存储键值对,并提供快速的查找、插入和删除操作。哈希表通过哈希函数将键映射到一个索引,从而实现高效的访问。

定义

哈希表是一种基于哈希函数的数据结构,它将键映射到一个数组的索引,从而实现快速的查找、插入和删除操作。

用途

哈希表常用于需要快速查找和计数的场景。例如,在本题中,我们使用哈希表来记录前缀和的余数出现的次数。

题目分析

解题思路

  1. 前缀和:我们可以计算序列的前缀和数组 prefix_sum,其中 prefix_sum[i] 表示从序列的开始到第 i 个元素的和。
  2. 哈希表:我们可以使用一个哈希表来记录前缀和的余数出现的次数。具体来说,如果 prefix_sum[i] % b == k,那么我们就记录余数 k 出现的次数。
  3. 计算结果:对于每个前缀和 prefix_sum[i],我们查找哈希表中余数为 (prefix_sum[i] % b) 的次数,并累加到结果中。

具体例子

假设我们有以下输入:

  • n = 5
  • b = 2
  • sequence = [1, 2, 3, 4, 5]

步骤1:初始化

  • prefix_sum = 0
  • remainder_count = {0: 1} (初始化余数为0的次数为1)
  • result = 0

步骤2:遍历序列

  1. 第一个元素 1

    • prefix_sum = 0 + 1 = 1
    • remainder = 1 % 2 = 1
    • remainder_count 中没有余数 1,所以不累加结果
    • 更新 remainder_countremainder_count = {0: 1, 1: 1}
  2. 第二个元素 2

    • prefix_sum = 1 + 2 = 3
    • remainder = 3 % 2 = 1
    • remainder_count 中有余数 1,累加结果:result = 0 + 1 = 1
    • 更新 remainder_countremainder_count = {0: 1, 1: 2}
  3. 第三个元素 3

    • prefix_sum = 3 + 3 = 6
    • remainder = 6 % 2 = 0
    • remainder_count 中有余数 0,累加结果:result = 1 + 1 = 2
    • 更新 remainder_countremainder_count = {0: 2, 1: 2}
  4. 第四个元素 4

    • prefix_sum = 6 + 4 = 10
    • remainder = 10 % 2 = 0
    • remainder_count 中有余数 0,累加结果:result = 2 + 2 = 4
    • 更新 remainder_countremainder_count = {0: 3, 1: 2}
  5. 第五个元素 5

    • prefix_sum = 10 + 5 = 15
    • remainder = 15 % 2 = 1
    • remainder_count 中有余数 1,累加结果:result = 4 + 2 = 6
    • 更新 remainder_countremainder_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)

详细解释

  1. 前缀和:前缀和数组 prefix_sum 用于存储从序列开始到当前元素的和。通过计算前缀和,我们可以快速计算任意子序列的和。
  2. 哈希表:哈希表 remainder_count 用于记录每个余数出现的次数。初始时,余数为0的次数为1,因为空序列的和为0,且0可以被任何数整除。
  3. 遍历序列:对于每个元素,我们更新前缀和 prefix_sum,并计算当前前缀和的余数 remainder
  4. 查找和更新:如果 remainder 已经在 remainder_count 中,说明存在之前的前缀和与当前前缀和的差值可以被 b 整除,因此累加结果。然后更新 remainder_count 中 remainder 的次数。

时间复杂度

  • 时间复杂度O(n),其中 n 是序列的长度。我们只需要遍历一次序列,并在每次遍历时进行常数时间的操作。
  • 空间复杂度O(b),其中 b 是给定的整数。哈希表 remainder_count 最多存储 b 个不同的余数。

总结

通过使用前缀和和哈希表,我们可以高效地计算出所有满足条件的连续子序列的数量。