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

66 阅读2分钟

问题描述

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

问题理解

我们需要找出给定序列中,有多少个连续子序列的和能够被一个给定的正整数 b 整除。

数据结构选择

为了高效地解决这个问题,我们可以使用前缀和的概念。前缀和数组 prefixSum 记录从序列开始到当前位置的和。

算法步骤

  1. 计算前缀和:遍历序列,计算每个位置的前缀和。
  2. 利用前缀和的性质:如果两个前缀和 prefixSum[i] 和 prefixSum[j] 对 b 取模的结果相同,那么 prefixSum[j] - prefixSum[i] 就是 b 的倍数。
  3. 使用哈希表:我们可以使用一个哈希表来记录每个前缀和对 b 取模的结果出现的次数。这样,当我们遍历到某个前缀和时,可以直接从哈希表中获取之前有多少个相同取模结果的前缀和,从而快速计算出满足条件的子序列数量。

具体步骤

  1. 初始化一个哈希表 modCount,用于记录前缀和对 b 取模的结果出现的次数。
  2. 遍历序列,计算前缀和,并对 b 取模。
  3. 将当前前缀和对 b 取模的结果加入哈希表,并累加之前相同取模结果的次数到结果中。
  4. 返回结果。

通过这种方式,我们可以在 O(n) 的时间复杂度内解决这个问题。

代码实现 // 初始化一个哈希表,用于记录前缀和对 b 取模的结果出现的次数 Map<Integer, Integer> modCount = new HashMap<>(); // 初始化前缀和为 0,并将其对 b 取模的结果放入哈希表 int prefixSum = 0; modCount.put(prefixSum % b, 1);

    int count = 0;
    
    // 遍历序列
    for (int num : sequence) {
        // 计算当前前缀和
        prefixSum += num;
        // 计算当前前缀和对 b 取模的结果
        int mod = prefixSum % b;
        
        // 如果 mod 为负数,将其转换为正数
        if (mod < 0) {
            mod += b;
        }
        
        // 如果哈希表中已经存在相同取模结果的前缀和,累加到结果中
        if (modCount.containsKey(mod)) {
            count += modCount.get(mod);
        }
        
        // 将当前前缀和对 b 取模的结果放入哈希表
        modCount.put(mod, modCount.getOrDefault(mod, 0) + 1);
    }
    
    return count;