BZOJ3238: [Ahoi2013]差异
【传送门:BZOJ3238】
简要题意:
给出一个长度为n的字符串,设Ti为以第i个字符为开头的后缀,lcp(x,y)为x字符串和y字符串的最长公共前缀
求$$\sum_{1<=i<j<=n}len(Ti)+len(Tj)-2*lcp(i,j)$$
题解:
后缀数组吧?
先打个板子
显然可以先把Σlen(Ti)+len(Tj)直接求出来
ans=(len-1)*(len+1)*len/2
接下来处理最长公共前缀和的问题
先求出height数组,表示字典序排第i-1个的后缀与排第i个的后缀的最长公共前缀
如果我们要求第i个到第j个的最长公共前缀,就等于min(height[i+1],height[i+2]....height[j])
我们就用两个单调栈来处理每个height值最多往左右延伸的长度,分别用l[i],r[i]记录
然后ans-=2*Σ(i-l[i]+1)*(r[i]-i+1)*height[i]
因为以i往左最多延伸l[i],往右最多延伸r[i]的情况下,包含i的区间有(i-l[i]+1)*(r[i]-i+1)个
还要注意一个地方,在求的时候,假如相邻的值相等,那么答案就会多算,所以有求r的时候,单调栈的条件要变成<=
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; char a[510000]; int s[510000]; int sa1[510000],sa2[510000]; int Rank[510000]; int Rsort[510000]; int tt[510000]; void getsa(int n,int m) { memcpy(Rank,s,sizeof(Rank)); memset(Rsort,0,sizeof(Rsort)); for(int i=1;i<=n;i++) Rsort[Rank[i]]++; for(int i=1;i<=m;i++) Rsort[i]+=Rsort[i-1]; for(int i=n;i>=1;i--) sa1[Rsort[Rank[i]]--]=i; int ln=1,p=0; while(p<n) { int k=0; for(int i=n-ln+1;i<=n;i++) sa2[++k]=i; for(int i=1;i<=n;i++) if(sa1[i]-ln>0) sa2[++k]=sa1[i]-ln; memset(Rsort,0,sizeof(Rsort)); for(int i=1;i<=n;i++) Rsort[Rank[i]]++; for(int i=1;i<=m;i++) Rsort[i]+=Rsort[i-1]; for(int i=n;i>=1;i--) sa1[Rsort[Rank[sa2[i]]]--]=sa2[i]; for(int i=1;i<=n;i++) tt[i]=Rank[i]; Rank[sa1[1]]=1;p=1; for(int i=2;i<=n;i++) { if(tt[sa1[i]]!=tt[sa1[i-1]]||tt[sa1[i]+ln]!=tt[sa1[i-1]+ln]) p++; Rank[sa1[i]]=p; } ln*=2;m=p; } } LL height[510000]; int list[510000]; int l[510000],r[510000]; int main() { scanf("%s",a+1); int len=strlen(a+1); for(int i=1;i<=len;i++) s[i]=a[i]-'a'+1; getsa(len,200); LL ans=LL(len-1)*LL(len+1)*LL(len)/2; height[1]=0;int k=0; for(int i=1;i<=len;i++) { int j=sa1[Rank[i]-1]; if(k>0) k--; while(a[j+k]==a[i+k])k++; height[Rank[i]]=k; } int tail=0; for(int i=1;i<=len;i++) { while(tail!=0&&height[list[tail]]>height[i]) r[list[tail--]]=i-1; list[++tail]=i; } while(tail!=0) r[list[tail--]]=len; for(int i=len;i>=1;i--) { while(tail!=0&&height[list[tail]]>=height[i]) l[list[tail--]]=i+1; list[++tail]=i; } while(tail!=0) l[list[tail--]]=1; LL sum=0; for(int i=1;i<=len;i++) sum+=LL(i-l[i]+1)*LL(r[i]-i+1)*height[i]; printf("%lld\n",ans-sum*2); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚