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

75 阅读3分钟

问题描述

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


测试样例

样例1:

输入:n = 3,b = 3,sequence = [1, 2, 3]
输出:3

样例2:

输入:n = 4,b = 5,sequence = [5, 10, 15, 20]
输出:10

样例3:

输入:n = 5,b = 2,sequence = [1, 2, 3, 4, 5]
输出:6

问题理解

我们需要计算一个长度为 n 的正整数序列中,有多少个连续子序列的和能够被一个给定的正整数 b 整除。

数据结构选择

为了高效地解决这个问题,我们可以使用以下数据结构:

  1. 前缀和数组:用于存储从序列开始到当前位置的和。
  2. 哈希表:用于存储前缀和的余数及其出现的次数。

算法步骤

  1. 初始化

    • 创建一个哈希表 prefixSumModCount,用于存储前缀和的余数及其出现的次数。
    • 初始化前缀和 prefixSum 为 0,并将 prefixSumModCount 中余数为 0 的计数初始化为 1。
  2. 遍历序列

    • 对于序列中的每个元素 num,更新前缀和 prefixSum
    • 计算当前前缀和 prefixSumb 的余数 mod
    • 如果 mod 为负数,将其调整为正数。
  3. 查找和更新哈希表

    • 如果哈希表中已经存在相同的余数 mod,说明存在子数组的和可以被 b 整除,将这些子数组的数量累加到 count 中。
    • 更新哈希表中当前余数 mod 的计数。
  4. 返回结果

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

复杂度分析

  • 时间复杂度O(n),因为我们只需要遍历一次序列。
  • 空间复杂度O(n),主要用于存储哈希表中的前缀和余数。

代码实现

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

public class Main {
    public static int solution(int n, int b, List<Integer> sequence) {
        // 创建一个哈希表来存储前缀和的余数及其出现的次数
        Map<Integer, Integer> prefixSumModCount = new HashMap<>();
        // 初始化前缀和为0,余数为0的情况
        prefixSumModCount.put(0, 1);
        
        int prefixSum = 0;
        int count = 0;
        
        for (int num : sequence) {
            prefixSum += num;
            int mod = prefixSum % b;
            
            // 如果mod为负数,调整为正数
            if (mod < 0) {
                mod += b;
            }
            
            // 如果哈希表中存在相同的余数,说明存在子数组的和可以被b整除
            if (prefixSumModCount.containsKey(mod)) {
                count += prefixSumModCount.get(mod);
            }
            
            // 更新哈希表中当前余数的计数
            prefixSumModCount.put(mod, prefixSumModCount.getOrDefault(mod, 0) + 1);
        }
        
        return count;
    }

    public static void main(String[] args) {
        // You can add more test cases here
        List<Integer> sequence = new ArrayList<>();
        sequence.add(1);
        sequence.add(2);
        sequence.add(3);
        System.out.println(solution(3, 3, sequence) == 3);
    }
}

总结

通过使用前缀和和哈希表,我们可以在 O(n) 的时间复杂度内解决这个问题,非常适合处理大规模数据。这种方法不仅高效,而且易于理解和实现,是解决此类问题的经典方法。

此外,与前缀和相对应的方法是差分,这是一种在O(n)时间内求和的方法,在未来也应该掌握。