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

164 阅读3分钟

题目描述

小M是一个五年级的小学生,今天他学习了整除的知识,想通过一些练习来巩固自己的理解。他写下了一个长度为 n 的正整数序列 a_0, a_1, ..., a_{n-1},然后想知道有多少个连续子序列的和能够被一个给定的正整数 b 整除。你能帮小M解决这个问题吗?

输入

  • 整数 n (1 ≤ n ≤ 10^5):序列的长度。
  • 整数 b (1 ≤ b ≤ 10^9):整除的目标值。
  • 长度为 n 的正整数序列 sequence,其中每个元素满足 1 ≤ a_i ≤ 10^9

输出

  • 输出满足条件的子序列数量。

示例

输入 1:

n = 3, b = 3
sequence = [1, 2, 3]

输出 1:

3

输入 2:

n = 4, b = 5
sequence = [5, 10, 15, 20]

输出 2:

10

输入 3:

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

输出 3:

6

思路1:暴力枚举法(O(n^2))

最直观的做法是暴力枚举所有可能的连续子数组,计算其和,判断是否能被 b 整除。这种方法的步骤如下:

  1. 对于每一对 ij0 ≤ j ≤ i < n),枚举所有从 sequence[j]sequence[i] 的子数组。
  2. 对每个子数组,计算其和,判断是否能够被 b 整除。
  3. 累计满足条件的子数组数量。

时间复杂度:由于我们有两个嵌套的循环,外层循环遍历 i,内层循环遍历 j,每次计算一个子数组的和。时间复杂度是 O(n^2)。

缺点:当 n 较大时,这种方法效率较低,可能导致超时。

def solution(n, b, sequence):
    count = 0
    for i in range(n):  # 外层循环
        for j in range(i + 1):  # 内层循环
            if sum(sequence[j:i+1]) % b == 0:  # 判断子数组和是否能被b整除
                count += 1
    return count

思路2:前缀和与模运算优化(O(n))

为了提升效率,可以使用 前缀和(Prefix Sum)取模运算 来将时间复杂度从 O(n^2) 降低到 O(n)。

关键点:

  1. 前缀和prefix[i] 表示从 sequence[0]sequence[i-1] 的和。子数组 [j, i-1] 的和可以通过 prefix[i] - prefix[j] 来计算。
  2. 模运算:我们需要判断子数组和是否能被 b 整除,等价于判断 (prefix[i] - prefix[j]) % b == 0。利用模的性质可以转换为判断 prefix[i] % b == prefix[j] % b
  3. 使用哈希表记录前缀和的模值的出现次数,遇到相同的模值时,说明存在一个子数组和能够被 b 整除。

时间复杂度:我们只需要遍历一次 sequence,每次更新前缀和并更新哈希表,时间复杂度为 O(n)。

from collections import defaultdict

def solution(n, b, sequence):
    count = 0
    prefix_sum = 0
    mod_count = defaultdict(int)
    
    # 初始化,prefix_sum = 0 mod b,表示空前缀的情况
    mod_count[0] = 1
    
    for num in sequence:
        prefix_sum += num
        mod_value = prefix_sum % b
        
        # 处理负数模值情况,因为 Python 的模运算对负数会有负结果
        if mod_value < 0:
            mod_value += b
        
        # 如果之前有相同模值的前缀和,那么说明有子序列的和能被 b 整除
        count += mod_count[mod_value]
        
        # 更新当前模值出现的次数
        mod_count[mod_value] += 1
    
    return count

总结

  1. 暴力枚举法:时间复杂度是 O(n^2),适用于小规模的 n,但是当 n 较大时会超时,因此不适合大数据量的情况。
  2. 前缀和与模运算优化:通过前缀和计算子数组的和,并利用模运算来判断是否能被 b 整除,时间复杂度优化为 O(n),适用于大规模的数据。

最终推荐的解法前缀和与模运算优化法,因为它具有更高的效率,能够在较大的数据范围内处理问题。