[APIO2014] [Uoj103] [Bzoj3676] Palindromes回文串 [Manacher,后缀数组]
用Manacher算法枚举回文子串,每次在后缀数组排序后的后缀数组中二分,因为用某一后缀和其他子串分别求匹配的长度,匹配长度在排序后该后缀的两侧具有单调性(匹配长度为min{H[x]|i<=x<=j},所以对于查询min(H[x])用ST表O(n)预处理,O(1)查询即可。Manacher时间复杂度O(n),后缀数组复杂度O(nlogn),总复杂度O(nlogn)。注意二分时的边界条件!
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <algorithm> using namespace std; long long Ans; int A[310000],B[310000],U[310000]; int SA[310000],*Rank,H[310000],Tmp[310000]; int lg2[310000],ST[310000][20],p[610000]; char str[610000]; void Get_H(const int n) { int i,j,k=0; for(i=0;i<n;H[Rank[i++]]=k) for(k?k--:k,j=SA[Rank[i]-1];str[i+k]==str[j+k];++k); for(i=2;i<=n;++i)lg2[i]=lg2[i>>1]+1; for(i=1;i<=n;++i)ST[i][0]=H[i]; for(j=1;(1<<j)<=n;++j) { for(i=1;i+(1<<j)-1<=n;++i) { ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]); } } return ; } int Query(const int l,const int r) { int temp=lg2[r-l+1]; return min(ST[l][temp],ST[r-(1<<temp)+1][temp]); } bool cmp(const int * s,const int a,const int b,const int l) { return s[a]==s[b] && s[a+l]==s[b+l]; } int* Get_SA(const int n,int m) { int i,j,_p,*x=A,*y=B; for(i=0;i<m;++i)U[i]=0; for(i=0;i<n;++i)U[x[i]=str[i]]++; for(i=1;i<m;++i)U[i]+=U[i-1]; for(i=n-1;i>=0;--i)SA[--U[x[i]]]=i; for(j=1,_p=1;_p<n;m=_p,j<<=1) { for(_p=0,i=n-j;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<n;++i)Tmp[i]=x[y[i]]; for(i=0;i<m;++i)U[i]=0; for(i=0;i<n;++i)U[Tmp[i]]++; for(i=1;i<m;++i)U[i]+=U[i-1]; for(i=n-1;i>=0;--i)SA[--U[Tmp[i]]]=y[i]; for(swap(x,y),_p=1,x[SA[0]]=0,i=1;i<n;++i) x[SA[i]]=cmp(y,SA[i-1],SA[i],j)?_p-1:_p++; } return x; } long long Calc(int l,int r,const int n) { l=(l-1)>>1,r=(r-2)>>1; int pos=Rank[l],L,R,temp=1; L=0,R=pos; while(L<R-1) { int mid=L+((R-L)>>1); if(Query(mid+1,pos)>=r-l+1)R=mid; else L=mid; } temp+=pos-R; L=pos,R=n+1; while(L<R-1) { int mid=L+((R-L)>>1); if(Query(pos+1,mid)>=r-l+1)L=mid; else R=mid; } temp+=L-pos; return (long long)temp*(r-l+1); } void Manacher(const int n) { int i,pos=0; for(i=n-1;i>=0;--i) { str[i+i+2]=str[i]; str[i+i+1]='#'; } str[0]='^';str[n<<1|1]='#';str[(n+1)<<1]='$'; for(i=1;i<=(n<<1|1);++i) { if(p[pos]+pos>i) p[i]=min(p[(pos<<1)-i],p[pos]+pos-i); else p[i]=1; while(str[i-p[i]]==str[i+p[i]]) { if(i+p[i]>p[pos]+pos) Ans=max(Ans,Calc(i-p[i],i+p[i],n)); p[i]++; } if(pos+p[pos]<i+p[i])pos=i; } return ; } int main() { int n; scanf("%s",str); n=strlen(str); Rank=Get_SA(n+1,256); Get_H(n); Manacher(n); printf("%lld",Ans); return 0; }