【BZOJ3238】[Ahoi2013]差异 后缀数组+单调栈 【BZOJ3238】[Ahoi2013]差异
Description
Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成
题解:先跑后缀数组得到height数组,然后我们为了得到∑LCP(i,j),可以转变成求每个height数组对答案做了多少贡献(也就是有多少对LCP(i,j)=height[i])。
根据height数组的定义,两个后缀的LCP=height[i]意味着rank[a]和rank[b]中间的所有height都大于等于height[i],那么我们用两次单调栈处理出height[i]两边第一个比height比i小的数,然后统计一下个数就行了
注意一下height相等的情况,不要重复计算,方法是在两次单调栈中一次用>=,一次用>就行了
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn=500010; int n,m; int r[maxn],sa[maxn],ra[maxn],rb[maxn],st[maxn],rank[maxn],h[maxn]; int q[maxn],t,ls[maxn],rs[maxn]; long long ans; char str[maxn]; void work() { int i,j,k,p,*x=ra,*y=rb; for(i=0;i<n;i++) st[x[i]=r[i]]++; for(i=1;i<m;i++) st[i]+=st[i-1]; for(i=n-1;i>=0;i--) sa[--st[x[i]]]=i; for(j=p=1;p<n;j<<=1,m=p) { for(i=n-j,p=0;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<m;i++) st[i]=0; for(i=0;i<n;i++) st[x[y[i]]]++; for(i=1;i<m;i++) st[i]+=st[i-1]; for(i=n-1;i>=0;i--) sa[--st[x[y[i]]]]=y[i]; for(swap(x,y),i=p=1,x[sa[0]]=0;i<n;i++) x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++; } for(i=1;i<n;i++) rank[sa[i]]=i; for(i=k=0;i<n-1;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); } int main() { scanf("%s",str),m=27,n=strlen(str); int i; for(i=0;i<n;i++) r[i]=str[i]-'a'+1; n++,work(),n--; h[0]=h[n+1]=-1; for(i=1,t=0;i<=n;i++) { while(t&&h[q[t]]>=h[i]) t--; ls[i]=q[t],q[++t]=i; } for(i=n,t=0,q[0]=n+1;i>=1;i--) { while(t&&h[q[t]]>h[i]) t--; rs[i]=q[t],q[++t]=i; } ans=(long long)n*(n-1)*(n+1)/2; for(i=1;i<=n;i++) ans-=(long long)h[i]*(i-ls[i])*(rs[i]-i)*2; printf("%lld",ans); return 0; }