连续子串

163 阅读3分钟

问题理解

我们需要计算一个长度为 n 的正整数序列中,有多少个连续子序列的和能够被一个给定的正整数 b 整除。这个问题可以转化为寻找满足特定条件的子序列数量,即子序列的和是 b 的倍数。

数据结构选择

  1. 前缀和:为了快速计算任意子序列的和,我们可以使用前缀和数组。前缀和数组的第 i 个元素表示原数组前 i 个元素的和。
  2. 哈希表:为了高效地查找和更新前缀和的余数,我们可以使用哈希表来存储每个余数的出现次数。

算法步骤

  1. 初始化

    • 创建一个哈希表 prefix_sum_mod,用于存储前缀和的余数及其出现次数。初始化 prefix_sum_mod[0] = 1,因为余数为0的情况至少出现一次。
    • 初始化 prefix_sum 为0,用于记录当前的前缀和。
    • 初始化 count 为0,用于记录满足条件的子序列数量。
  2. 遍历序列

    • 对于序列中的每个元素 num,更新 prefix_sum 为 prefix_sum + num
    • 计算当前前缀和 prefix_sum 除以 b 的余数 mod
    • 如果 mod 在哈希表中已经存在,说明存在一个子序列的和可以被 b 整除,将对应的子序列数量加到 count 中。
    • 更新哈希表 prefix_sum_mod,将 mod 的出现次数加1。
  3. 返回结果

    • 遍历结束后,返回 count,即满足条件的子序列数量。

具体步骤解析

  1. 初始化

    • 我们首先创建一个哈希表 prefix_sum_mod,用于存储前缀和的余数及其出现次数。哈希表的键是余数,值是该余数出现的次数。初始化 prefix_sum_mod[0] = 1,因为余数为0的情况至少出现一次。
    • 初始化 prefix_sum 为0,用于记录当前的前缀和。prefix_sum 表示从序列的第一个元素到当前元素的和。
    • 初始化 count 为0,用于记录满足条件的子序列数量。count 最终将是我们需要返回的结果。
  2. 遍历序列

    • 我们遍历序列中的每个元素 num,更新 prefix_sum 为 prefix_sum + num。这样,prefix_sum 始终表示从序列的第一个元素到当前元素的和。
    • 计算当前前缀和 prefix_sum 除以 b 的余数 mod。余数 mod 表示当前前缀和除以 b 后的余数。
    • 如果 mod 在哈希表 prefix_sum_mod 中已经存在,说明存在一个子序列的和可以被 b 整除。具体来说,如果 prefix_sum_mod[mod] 大于0,说明之前存在一个前缀和的余数与当前前缀和的余数相同,这意味着这两个前缀和之间的子序列的和是 b 的倍数。我们将 prefix_sum_mod[mod] 的值加到 count 中。
    • 更新哈希表 prefix_sum_mod,将 mod 的出现次数加1。这样,我们可以在后续的遍历中继续使用这个信息。
  3. 返回结果

    • 遍历结束后,返回 count,即满足条件的子序列数量。count 记录了所有满足条件的子序列的数量。

复杂度分析

  • 时间复杂度:O(n),因为我们只需要遍历一次序列。每次遍历中,我们进行常数时间的操作,包括更新前缀和、计算余数、查找和更新哈希表。
  • 空间复杂度:O(n),哈希表最多存储 n 个不同的余数。在最坏情况下,每个前缀和的余数都不同,因此哈希表的大小为 n

总结

通过使用前缀和和哈希表,我们可以在 O(n) 的时间复杂度内解决这个问题,有效地避免了暴力解法的 O(n^2) 时间复杂度。前缀和数组帮助我们快速计算任意子序列的和,而哈希表则帮助我们高效地查找和更新前缀和的余数。通过这种方法,我们可以准确地计算出满足条件的子序列数量。