【后缀数组】【线段树】poj3974 Palindrome
考虑奇数长度的回文,对于字符串上的每个位置i,如果知道从i开始的后缀和到i为止的前缀反转后的字符串的lcp长度的话,也就知道了以第i个字符为对称中心的最长回文的长度了。因此,我们用在S中不会出现的字符将S和S反转后的字符串拼接起来,得到字符串S',计算S'的sa。于是,从i开始的后缀和到i为止的前缀反转后的字符串就都是S'中的后缀了,利用高度数组,可以轻易地求得它们最长公共前缀的长度。
对于长度为偶数的回文的处理也基本相同。
哈哈哈,MLE+TLE不可避。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 2000001 #define INF 2147483647 char s[N]; int tong['z'+1],t[N],n,t2[N],sa[N],lcp[N],rank[N]; bool cmp(int *y,int i,int k) { return ((y[sa[i-1]]==y[sa[i]])&&((sa[i-1]+k>=n?-1:y[sa[i-1]+k])==(sa[i]+k>=n?-1:y[sa[i]+k]))); } void build_sa(int range) { int *x=t,*y=t2; memset(tong,0,sizeof(int)*range); for(int i=0;i<n;++i) tong[x[i]=s[i]]++; for(int i=1;i<range;++i) tong[i]+=tong[i-1]; for(int i=n-1;i>=0;--i) sa[--tong[x[i]]]=i; for(int k=1;k<=n;k<<=1) { int p=0; for(int i=n-k;i<n;++i) y[p++]=i; for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k; memset(tong,0,sizeof(int)*range); for(int i=0;i<n;++i) tong[x[y[i]]]++; for(int i=1;i<range;++i) tong[i]+=tong[i-1]; for(int i=n-1;i>=0;--i) sa[--tong[x[y[i]]]]=y[i]; swap(x,y); p=1; x[sa[0]]=0; for(int i=1;i<n;++i) x[sa[i]]=cmp(y,i,k)?p-1:p++; if(p>=n) break; range=p; } } void get_lcp() { int k=0; for(int i=0;i<n;++i) rank[sa[i]]=i; for(int i=0;i<n;++i) if(rank[i]) { if(k) --k; int j=sa[rank[i]-1]; while(s[i+k]==s[j+k]) ++k; lcp[rank[i]]=k; } } int minv[N<<2]; void buildtree(int rt,int l,int r) { if(l==r) { minv[rt]=lcp[l]; return; } int m=(l+r>>1); buildtree(rt<<1,l,m); buildtree(rt<<1|1,m+1,r); minv[rt]=min(minv[rt<<1],minv[rt<<1|1]); } int query(int ql,int qr,int rt,int l,int r) { if(ql<=l&&r<=qr) return minv[rt]; int m=(l+r>>1),res=INF; if(ql<=m) res=min(res,query(ql,qr,rt<<1,l,m)); if(m<qr) res=min(res,query(ql,qr,rt<<1|1,m+1,r)); return res; } int main() { int T=0; while(1) { scanf("%s",s); if(s[0]=='E') break; ++T; int l=strlen(s); n=(l<<1|1); s[l]='$'; reverse_copy(s,s+l,s+l+1); build_sa('z'+1); get_lcp(); buildtree(1,1,n-1); int ans=0; for(int i=0;i<l;++i)//奇数长度回文 ans=max(ans,(query(min(rank[i],rank[n-i-1])+1, max(rank[i],rank[n-i-1]),1,1,n-1)<<1)-1); for(int i=1;i<l;++i)//偶数数长度回文 ans=max(ans,(query(min(rank[i],rank[n-i])+1, max(rank[i],rank[n-i]),1,1,n-1)<<1)); printf("Case %d: %d\n",T,ans); } return 0; }
——The Solution By AutSky_JadeK From UESTC
转载请注明出处:http://www.cnblogs.com/autsky-jadek/