前缀和(区间求和快)
1.要点
[[前缀和]]
a[0]=0,prefix[0]=0;
for(int i=1;i<=n;i++){
prefix[i]=prefix[i-1]+a[i];
}
sum(l,r)=prefix[r]-prefix[l-1];
重点:
前缀和:预处理算法,a为静态数组,即在区间和查询过程中不会进行修改
差分:先区间修改,再区间查询
树状数组或线段树:一边修改,一边查询
2.题目
2020子串分值和
思路:
跟前缀和没啥关系,暴力两层遍历+map哈希能拿一些分,得满分要用last数组记录每个字母上一次出现位置,然后遍历一次字符串(字符串之前先加个空格),每个字母左边到l[i]+1
,保证第一次出现,共i-l[i]
种情况,右边到n,共n-i+1
种情况,两侧互相独立,相乘即可
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+9;
int l[N],p[26];
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
string s;
getline(cin,s);
//下面两个顺序不能反
int n=s.size();
s=" "+s;
ll res=0;
for(int i=1;i<=n;i++){
int t=s[i]-'a';
l[i]=p[t];
p[t]=i;
}
for(int i=1;i<=n;i++){
//左边到last[i]+1,共i-last[s[i]]种可能,右边到n,共n-i+1种可能
res+=(ll)(i-l[i])*(n-i+1);
}
cout<<res;
return 0;
}
2020子串分值
![[字母贡献度.png]]本质为上面字母贡献度,子串分值和为右侧为n的特殊情况,本题右侧没有字母则为n+1,需要预处理每个位置字母的左右边界,和前缀和类似
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+9;
int l[N],r[N],p[26];
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
string s;
getline(cin,s);
int n=s.size();
s=" "+s;
ll res=0;
for(int i=1;i<=n;i++){
int t=s[i]-'a';
l[i]=p[t];//l[i]为对于s[i],左侧相同字母出现位置,若没有则为0
p[t]=i;//更新位置
}
for(int i=0;i<26;i++) p[i]=n+1;//r[s[i]]没有则为n+1
for(int i=n;i>=1;i--){
int t=s[i]-'a';
r[i]=p[t];//r[i]为对于s[i],右侧相同字母出现位置,若没有则为n+1
p[t]=i;//更新位置
}
for(int i=1;i<=n;i++){
res+=(ll)(i-l[i])*(r[i]-i);//只考虑单个字母出现一次的左边界与右边界,乘法原理
}
cout<<res;
return 0;
}