题目:# 连续子串和的整除问题
问题描述
小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;
}
前缀和的思想可以应用于许多场景,特别是需要频繁进行区间求和的情况。以下是一些典型应用:
- 区间求和问题:可以通过前缀和快速计算数组任意子区间的和。例如,求解数组
arr中从第l到第r个元素的和。 - 二维前缀和:在二维数组中,前缀和也可以进行扩展,称为二维前缀和。它的思想和一维前缀和类似,可以快速计算矩阵任意子矩阵的和。
- 统计问题:前缀和可以用于统计区间中满足某些条件的元素数量,如求数组中某区间内小于某个值的元素个数。
- 求解最大子数组和:结合前缀和的思想,求解最大子数组和问题时,可以通过在前缀和数组中利用动态规划和贪心策略来优化求解过程。