版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88955617
题面:
题目分析:
前面的
考虑后面怎么求。
只需要把后缀数组中的height值拿来乱搞。
两个排名分别为i,j的后缀,它们的LCP就是height[i+1,j]的最小值。
枚举排名从小到大,维护一个height值递增的栈,把栈中的贡献存在ret中,每次比较栈顶和当前height值的大小,如果比当前值小就ret-=它的那段贡献(对应的长度*height值)然后弹出,最后ret+=当前的height对应的区间长度*height值,然后ans-=ret*2。
搞一个结构体存长度和height值就好了。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 500005
using namespace std;
int ary[4][maxn],b[maxn],height[maxn];
int *sa=ary[0],*rk=ary[1],*nsa=ary[2],*nrk=ary[3];
void cal_sa(int n,int m,int *a){
for(int i=1;i<=n;i++) b[a[i]]++;
for(int i=1;i<=m;i++) b[i]+=b[i-1];
for(int i=1;i<=n;i++) sa[b[a[i]]--]=i;
for(int i=1;i<=n;i++) rk[sa[i]]=rk[sa[i-1]]+(a[sa[i-1]]!=a[sa[i]]);
for(int k=1;k<n&&rk[sa[n]]<n;k<<=1){
for(int i=1;i<=n;i++) b[rk[sa[i]]]=i;
for(int i=n;i>=1;i--) if(sa[i]>k) nsa[b[rk[sa[i]-k]]--]=sa[i]-k;
for(int i=n-k+1;i<=n;i++) nsa[b[rk[i]]--]=i;
for(int i=1;i<=n;i++) nrk[nsa[i]]=nrk[nsa[i-1]]+(rk[nsa[i-1]]!=rk[nsa[i]]||rk[nsa[i-1]+k]!=rk[nsa[i]+k]);
swap(sa,nsa),swap(rk,nrk);
}
}
void cal_height(int n,int *a){
for(int i=1,k=0;i<=n;i++) if(rk[i]!=1){
if(k) k--;
for(int j=sa[rk[i]-1];a[j+k]==a[i+k];k++);
height[rk[i]]=k;
}
}
int n,a[maxn],top;
long long ans,ret;
char s[maxn];
struct node{
int x,len;
node(){}
node(int x,int len):x(x),len(len){}
}q[maxn];
int main()
{
scanf("%s",s+1),n=strlen(s+1);
for(int i=1;i<=n;i++) a[i]=s[i]-'a'+1;
cal_sa(n,26,a),cal_height(n,a);
for(int i=2;i<=n;i++){
int len=1;
while(top&&q[top].x>=height[i]) len+=q[top].len,ret-=1ll*q[top].len*q[top].x,top--;
ret+=1ll*len*height[i];
ans+=ret;
q[++top]=node(height[i],len);
}
printf("%lld\n",1ll*n*(n+1)/2*(n-1)-2*ans);
}
PS:我的cal_height函数没有判断i+k<=n,所以必须保证串的末尾的后面那个元素不等于串中的元素。所以转换的时候是a[i]=s[i]-‘a’+1而不能是a[i]=s[i]-‘a’
Upd:对于将串反转之后用后缀自动机构建后缀树的解法可以看这里(这篇blog里面似乎没有将串反转。。数据太水??)