The basic idea of prefix sum

(1) Definition of prefix sum

    Suppose there is a string ABCDE, then A, AB, ABC, ABCD, ABCDE are the prefixes of this word, starting from the first character and splicing backwards in sequence. And E, ED, EDC, EDCB, EDCBA are called suffixes of this word.

    Then for the prefix of an int type array, such as the array a=[0,12,62,33,4,55], we maintain an array sum composed of the sum of the prefix, and sum[i] represents a[0 in the array ]~a[i] and. Where a[0] defaults to 0, if:

  • sum[0]=a[0]=0
  • sum[1]=a[0]+a[1]
  • sum[2]=a[0]+a[1]+a[2]
  • sum[3]=a[0]+a[1]+a[2]+a[3]
  • sum[4]=a[0]+a[1]+a[2]+a[3]+a[4]

  Then sum is the prefix and array of array a.

(2) The role of the prefix sum

  The prefix sum can be stored by preprocessing (maintaining while inputting), which can greatly reduce the complexity of the query world. Because the main purpose of the prefix sum is to find the size of the sum of the subarrays. For example, the sum of elements a[1] to a[3]: a[1]+a[2]+a[3], this for loop takes O(n) time, and the prefix and sum[ 3]-sum[0] to solve is the complexity of O(1), why sum[3]-sum[0] is the sum of a[1] to a[3]?

  Let me explain, give a more vivid example, find the sum of elements a[3] to a[5]: a[3]+a[4]+a[5], the prefix sum is sum[5] -sum[2], why? Let's take it apart:

sum[5]=a[0]+a[1]+a[2]+a[3]+a[4]+a[5];

sum[2]=a[0]+a[1]+a[2];

  Seeing nothing, the previous three a[0]+a[1]+a[2] can be offset, it becomes a[3]+a[4]+a[5], so using the prefix sum Thought, the time complexity is optimized to the optimum.

(3) Application of prefixes and

  Let's use a question to illustrate the application of the prefix sum.

  Given an integer array of length n and an integer k, you need to find the number of consecutive subarrays in the array that sum to k,

Test sample:

enter:

5 3
1 1 2 1 1

output:

2

  Idea 1:

    Use the for loop to violently enumerate the sub-arrays, and sum + count, the time complexity is O(n^3). (If the data is greater than 100, this idea is definitely Time Limit Exceeded (time overtime), so it needs to be optimized)!

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,k; //定义 
	scanf("%d%d",&n,&k); //输入 
	int a[n],sum=0; //定义数组和计数器 
	for(int i=0;i<n;i++) //for循环输入 
	  scanf("%d",a[i]); //输入下标为i的数 
	for(int i=0;i<n;i++){ //区间i枚举 
		for(int j=0;j<n;j++){ //区间j枚举 
			int ans=0; //区间和计数数组 
			for(int x=i;x<=j;x++) //求出区间[i,j]的和 
			  ans+=a[i]; //计数 
			if(ans==k) //比较 
			  sum++; //计数	  
		}
	}
	cout<<sum<<endl; //输出 
	return 0;
}

Idea 2:

  We can also use the prefix sum to solve this problem. First, preprocess the prefix array sum of a, calculate the sum of the sub-arrays each time, and then perform the double-loop interval enumeration, and then compare it with k. The time complexity is O (n^2). (If the data is not greater than 1500, it can be AC, and there is still room for optimization).

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,k; //定义 
	scanf("%d%d",&n,&k); //输入 
	int sum[n+1]={0},ans=0; //定义前缀和数组sum和计数器ans 
	sum[0]=0; //将下标为0的地址初始化为0  
	for(int i=1;i<=n;i++){ //进行循环n次读入 
		int a; //定义 
		scanf("%d",&a); //输入 
		sum[i]=sum[i-1]+a; //求前缀和 
	}
	for(int i=1;i<=n;i++) //区间枚举i 
	  for(int j=0;j<n;j++) //区间枚举j 
	    if(sum[i]-sum[j]==k) //求出区间[i,j]的和与k比较 
	      ans++; //计数器加1 
	cout<<ans<<endl; //输出计数器 
	return 0; //结束 
}

Idea 3:

  We can use map (a class defined by the STL library), prefix and optimize. In the idea of ​​using the prefix sum alone, we require whether the sum of a subarray with the subscript i at the end is k, we need to traverse j from 0 to i-1 to find whether there is sum[i]-k= sum[j]. Then we only use map to save the prefix sum of the first i elements, save the value of the prefix sum before the number of occurrences (>i), and finally judge whether the map contains sum[i]-k.

  The time complexity is O(n), which is already very fast, and the data can be AC ​​if it is as large as several million.

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,k; //定义 
	scanf("%d%d",&n,&k); //输入 
	int sum[n+1]={0},ans=0; //定义前缀和数组sum和计数器ans 
	sum[0]=0; //将下标为0的地址初始化为0  
	map<int,int> p; //定义map 
	for(int i=1;i<=n;i++){ //进行循环n次读入 
		int a; //定义 
		scanf("%d",&a); //输入 
		sum[i]=sum[i-1]+a; //求前缀和 
		p[sum[i]]++; //进行存储 
	}
	for(int i=0;i<n;i++) //进行区间判断 
	  ans+=p[sum[i]+k]; //计数 
	cout<<ans<<endl; //输出 
	return 0; //结束 
}

Summarize:

  Prefix sum is a very useful algorithm that can greatly optimize the time complexity. I hope you can learn more. I will talk about some prefix sum exercises in the next few articles.

Guess you like

Origin blog.csdn.net/wo_ai_luo_/article/details/129819793