题意:
给定一个长度为n的字符串 S,令Ti表示它从第i个字符开始的后缀,求:
解法:
显然前面的T(i)和T(j)是可以提出来直接算的,
考虑每个后缀作为T(i)和T(j)的次数:
1.作为T(i),那么对应n-i个T(j),因此次数为n-i
2.作为T(j),那么对应i-1个T(i),因此次数为i-1
综上得:每个后缀出现的总次数为n-i+i-1=n-1次
因此前面一段T(i)和T(j)部分的贡献为1(n-1)+2(n-1)+3(n-3)…n(n-1),(长度为1的后缀到长度为n的后缀)
即n(n+1)/2(n-1)。
然后考虑如何计算式子后面的lcp部分:
后缀数组中height[]的重要结论:lcp(i,j)=min{height[i+1],height[i+2]…height[j] }
那么问题可以变为,计算每个height是多少对(i,j)的lcp,即计算每个height的贡献,
因为lcp是对height取min,所以就是计算每个height[i]能作为多少个区间的最小值,
这是一个经典的单调栈问题,计算出height[i]作为最小值向左和向右的最大扩展位置L[i]和R[i],
那么区间数量就是(i-L[i]+1)*(R[i]-i+1),
设区间数量为cnt,那么对答案的贡献就是-2* height[i]*cnt
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=4e6+5;
struct SA{
static const int N=4e6+5;
char s[N];
int sa[N],rk[N],oldrk[N<<1],id[N],px[N],cnt[N];
int ht[N];
int n;//字符串长度
int m=300;//初始字符集大小
bool cmp(int x,int y,int w){
return oldrk[x]==oldrk[y]&&oldrk[x+w]==oldrk[y+w];
}
void getSA(){
int i,p,w;
for(i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
//
for(w=1;w<n;w<<=1,m=p){
for(p=0,i=n;i>n-w;i--)id[++p]=i;
for(i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
memset(cnt,0,sizeof cnt);
for(i=1;i<=n;i++)cnt[px[i]=rk[id[i]]]++;
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--)sa[cnt[px[i]]--]=id[i];
memcpy(oldrk,rk,sizeof rk);
for(p=0,i=1;i<=n;i++){
rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
}
}
}
void getHT(){
for(int i=1,k=0;i<=n;i++){
if(k)k--;
while(s[i+k]==s[sa[rk[i]-1]+k])k++;
ht[rk[i]]=k;
}
}
}sa;
int l[maxm],r[maxm];
signed main(){
//input
scanf("%s",sa.s+1);
sa.n=strlen(sa.s+1);
//
sa.getSA();
sa.getHT();
//单调栈部分
sa.ht[0]=sa.ht[sa.n+1]=-1; //保证全部元素出栈
stack<int>stk;
for(int i=1;i<=sa.n+1;i++){
while(!stk.empty()&&sa.ht[stk.top()]>=sa.ht[i]){
r[stk.top()]=i-1;
stk.pop();
}
stk.push(i);
}
while(!stk.empty())stk.pop();
for(int i=sa.n;i>=0;i--) {
while(!stk.empty()&&sa.ht[stk.top()]>sa.ht[i]){
l[stk.top()]=i+1;
stk.pop();
}
stk.push(i);
}
//计算答案部分
ll ans=1ll*sa.n*(sa.n+1)/2*(sa.n-1);
for(int i=1;i<=sa.n;i++){
ll temp=1ll*sa.ht[i]*(i-l[i]+1)*(r[i]-i+1);
ans-=2*temp;
}
cout<<ans<<endl;
return 0;
}