[BZOJ3238][AHOI2013]差异(后缀数组)
求和式的前两项可以直接算,问题是对于每对i,j计算LCP。
一个比较显然的性质是,LCP(i,j)是h[rk[i]+1~rk[j]]中的最小值。
从h的每个元素角度考虑,就是对每个h计算有多少对i,j以它为最小值。
在h中使用单调栈统计左右比它小的第一个元素,相乘得到结果。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=1000010,inf=1000000000; 9 ll ans; 10 char s[N]; 11 int n,top,x[N],y[N],c[N],sa[N],h[N],rk[N],l[N],r[N],stk[N]; 12 int Cmp(int a,int b,int l){ return a+l<=n && b+l<=n && y[a]==y[b] && y[a+l]==y[b+l]; } 13 14 void build_sa(int m){ 15 memset(y,0,sizeof(y)); 16 rep(i,0,m) c[i]=0; 17 rep(i,1,n) c[x[i]=s[i]-'a'+1]++; 18 rep(i,1,m) c[i]+=c[i-1]; 19 for (int i=n; i; i--) sa[c[x[i]]--]=i; 20 for (int k=1,p=0; p<n; k<<=1,m=p){ 21 p=0; 22 rep(i,n-k+1,n) y[++p]=i; 23 rep(i,1,n) if (sa[i]>k) y[++p]=sa[i]-k; 24 rep(i,0,m) c[i]=0; 25 rep(i,1,n) c[x[y[i]]]++; 26 rep(i,1,m) c[i]+=c[i-1]; 27 for (int i=n; i; i--) sa[c[x[y[i]]]--]=y[i]; 28 rep(i,1,n) y[i]=x[i]; p=1; x[sa[1]]=1; 29 rep(i,2,n) x[sa[i]]=Cmp(sa[i-1],sa[i],k) ? p : ++p; 30 } 31 } 32 33 void get(){ 34 int k=0; 35 rep(i,1,n) rk[sa[i]]=i; 36 rep(i,1,n){ 37 for (int j=sa[rk[i]-1]; i+k<=n && j+k<=n && s[i+k]==s[j+k]; k++); 38 h[rk[i]]=k; if (k) k--; 39 } 40 } 41 42 void solve(){ 43 rep(i,1,n) ans+=1ll*i*(n-1); 44 h[0]=-inf; 45 rep(i,1,n){ 46 while (h[i]<=h[stk[top]]) top--; 47 if (!stk[top]) l[i]=1; else l[i]=stk[top]+1; 48 stk[++top]=i; 49 } 50 h[n+1]=-inf; top=0; stk[0]=n+1; 51 for (int i=n; i; i--){ 52 while (h[i]<h[stk[top]]) top--; 53 if (stk[top]==n+1) r[i]=n; else r[i]=stk[top]-1; 54 stk[++top]=i; 55 } 56 rep(i,1,n) ans-=2ll*(i-l[i]+1)*(r[i]-i+1)*h[i]; 57 } 58 59 int main(){ 60 freopen("bzoj3238.in","r",stdin); 61 freopen("bzoj3238.out","w",stdout); 62 scanf("%s",s+1); n=strlen(s+1); 63 build_sa(300); get(); solve(); printf("%lld\n",ans); 64 return 0; 65 }