題意:
給你一個串,求所有不同字串的貢獻和,每種字串的貢獻,k*(k+1)/2,k爲該子串出現的次數
題解:
aaaa這個串中,a的貢獻是10,即4+3+2+1,第一個a的單獨貢獻爲4,第二個a的貢獻是3......每個串的貢獻就是跟他之後的子串以及自己匹配一次。記cnt爲到這一位的該子串個數,一開始爲0,ans爲貢獻和,到1:cnt+=1,ans+=cnt;到2:cnt+=1,ans+=cnt,跟之前所有出現過的子串作此匹配;到3:cnt+=1,ans+=cnt;到4:cnt+=1,ans+=cnt
一開始ans爲n*(n+1)/2,每個串自己跟自己匹配一次的次數,之後求解不用再算這部分。
每個串後面的串個數,可以用單調棧做一下,基本過程就參照aaaa這個串a子串的貢獻求解方法。
關於height數組的性質。在i,向左找到第一個j1使,height[j1]<height[i],向右找到第一個j2,
使height[j2]<height[i]。j2-j1-1的值等於長度爲height[i]的串出現了多少次。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,inf=0x3f3f3f3f;
int sa[N];
int rk[N];
int tmp[N];
int lcp[N];
char s[N];
int n,k;
bool cmp(int i,int j){
if(rk[i] != rk[j]) return rk[i]<rk[j];
else
{
int ri=i+k<=n?rk[i+k]:-1;
int rj=j+k<=n?rk[j+k]:-1;
return ri<rj;
}
}
void build(char *s,int *sa)
{
n=strlen(s);
for(int i=0;i<=n;i++){
sa[i]=i;
rk[i]=i<n?s[i]:-1;
}
for(k=1;k<=n;k*=2){
sort(sa,sa+n+1,cmp);
tmp[sa[0]]=0;
for(int i=1;i<=n;i++){
tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
}
for(int i=0;i<=n;i++){
rk[i]=tmp[i];
}
}
}
void LCP(char *s,int *sa,int *lcp){
n=strlen(s);
for(int i=0;i<=n;i++) rk[sa[i]]=i;
int h=0;
lcp[0]=0;
for(int i=0;i<n;i++){
int j=sa[rk[i]-1];
for (h ? h-- : 0; j + h < n&&i + h < n&&s[j + h] == s[i + h]; h++);
lcp[rk[i]-1] = h;
}
}
struct node{
ll x,y;
}st[N];
int main() {
scanf("%s",s);
n=strlen(s);
build(s,sa);
LCP(s,sa,lcp);
//printf("@\n");
ll cnt=0,ans=(ll)(n+1)*(ll)n/2,now=0;
for(int i=0;i<n;i++){
node tmp;
tmp.x=lcp[i];
//printf("!%d\n",lcp[i]);
tmp.y=1;
while(now>0&&st[now].x>tmp.x){
tmp.y+=st[now].y;
cnt-=st[now].x*st[now].y;
now--;
}
st[++now]=tmp;
cnt+=tmp.x*tmp.y;
ans+=cnt;
}
cout<<ans<<endl;
return 0;
}