青训营X豆包MarsCode 技术训练营第一课 | 豆包MarsCode AI 刷题 | 前缀和

85 阅读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

 这道题用前缀和+滑动窗口的思想去做。

前缀和的原理如下

前缀和(Prefix Sum)是一种常用的算法思想,主要用于快速计算数组(或其他数据结构)中某一子区间的元素之和。其核心思想是通过预处理将原始数据转换成一个新的数组,使得后续的查询操作能够在常数时间内完成

例如,我们要统计 vec[i] 这个数组上的区间和。

首先,我们根据原始数组计算出前缀和数组。计算的时间复杂度是 O(n),其中 n 是数组的长度。

我们先做累加,即 p[i] 表示 下标 0 到 i 的 vec[i] 累加 之和。

如图(图源见水印):

一旦我们得到了前缀和数组,就可以通过简单的减法快速计算任意子区间的和。

如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。也就是说前缀和的数组上保存着原数组连续位置的和,所以前缀和适用于求解数组中的连续问题。

该题目正好跟数组的连续问题有关,我们使用一个窗口在前缀和数组中滑动,统计能被整除的连续子数组和就可以求解。

public static int solution(int n, int b, List<Integer> sequence) {
        // 前缀和求解?正整数数列
        if(sequence.size()==1)
        {
            if(sequence.get(0)%b==0)
            return 1;
            else
            return 0;
        }
        int[] prefix=new int[sequence.size()+1];
        int i,j;//左右指针
        int sum=0;
        for(i=0;i<sequence.size();i++)
        {
            prefix[i]=sum;//第一个数是0
            sum+=sequence.get(i);
        }
        prefix[i]=sum;
        i=0;
        //j=i+1;
        int len=0;
        while(i<sequence.size())//3
        {
            for(j=i+1;j<=sequence.size();j++)
            {
                if((prefix[j]-prefix[i])%b==0)
                {
                    len++;
                }
            }
            i++;//往右挪动
        }
        return len;
    }

前缀和的思想可以应用于许多场景,特别是需要频繁进行区间求和的情况。以下是一些典型应用:

  1. 区间求和问题:可以通过前缀和快速计算数组任意子区间的和。例如,求解数组 arr 中从第 l 到第 r 个元素的和。
  2. 二维前缀和:在二维数组中,前缀和也可以进行扩展,称为二维前缀和。它的思想和一维前缀和类似,可以快速计算矩阵任意子矩阵的和。
  3. 统计问题:前缀和可以用于统计区间中满足某些条件的元素数量,如求数组中某区间内小于某个值的元素个数。
  4. 求解最大子数组和:结合前缀和的思想,求解最大子数组和问题时,可以通过在前缀和数组中利用动态规划和贪心策略来优化求解过程。