bzoj3676: [Apio2014]回文串
传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3676
思路:首先要知道一个结论,本质不同的回文串的个数是O(n)的。
从manacher的过程就可以看出来,使最远边界扩展的回文串才是与之前本质不同的,边界只会扩展到n,所以个数是O(n)的
然后对于每个本质不同的字符串,在后缀数组里向上向下二分,找出它的出现次数即可得到答案。
(为什么正常的ST表TLE了....非得改成莫名其妙的O(logn)回答的才AC...)
#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> const int maxn=600010; const double eps=1e-10; using namespace std; int n,t1[maxn],t2[maxn],rank[maxn],sa[maxn],sum[maxn],h[maxn],st[maxn>>1][20],f[maxn];char s[maxn],b[maxn];long long ans; void getsa(){ int *x=t1,*y=t2,p=0,m=255; for (int i=1;i<=n;i++) sum[x[i]=s[i]]++; for (int i=1;i<=m;i++) sum[i]+=sum[i-1]; for (int i=1;i<=n;i++) sa[sum[x[i]]--]=i; for (int j=1;p<n;j<<=1,m=p){ p=0; for (int i=n-j+1;i<=n;i++) y[++p]=i; for (int i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j; memset(sum,0,sizeof(sum)); for (int i=1;i<=n;i++) sum[x[y[i]]]++; for (int i=1;i<=m;i++) sum[i]+=sum[i-1]; for (int i=n;i;i--) sa[sum[x[y[i]]]--]=y[i]; swap(x,y),x[sa[1]]=p=1; for (int i=2;i<=n;i++){ if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j]) p++; x[sa[i]]=p; } } memcpy(rank,x,sizeof(rank)); } void geth(){ for (int i=1,j=0;i<=n;i++){ if (rank[i]==1) continue; while (s[i+j]==s[sa[rank[i]-1]+j]) j++; h[rank[i]]=j; if (j>0) j--; } } void getst(){ for (int i=1;i<=n;i++) st[i][0]=h[i]; for (int j=1;(1<<j)<=n;j++) for (int i=1;i+(1<<(j-1))-1<=n;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } inline int getmin(int l,int r){ /*int k=log2(r-l+1); return min(st[l][k],st[r-(1<<k)+1][k]);*/ int len=r-l+1,k=0,now=1; for (int i=0;i<=20;i++){ if (now*2>=len){k=i;break;} now<<=1; } return min(st[l][k],st[r-(1<<k)+1][k]); } int query(int x,int k){ int l,r,mid,ansl,ansr; if (h[x+1]<k) ansr=x; else{ l=x+1,r=n; while (l<=r){ mid=(l+r)>>1; if (getmin(x+1,mid)>=k) ansr=mid,l=mid+1; else r=mid-1; } } if (h[x]<k) ansl=x; else{ l=1,r=x-1; while (l<=r){ mid=(l+r)>>1; if (getmin(mid+1,x)>=k) ansl=mid,r=mid-1; else l=mid+1; } } return ansr-ansl+1; } void manacher(){ int i,mx=1,id=1; for (b[0]='$',b[1]='#',i=1;i<=n;i++) b[i<<1]=s[i],b[(i<<1)|1]='#'; n=(n<<1)|1;b[n+1]='\0'; //printf("%s\n",b); for (int i=1;i<=n;i++){ f[i]=min(f[(id<<1)-i],mx-i); for (;i>=f[i]&&b[i-f[i]]==b[i+f[i]];) f[i]++; f[i]--; if (i+f[i]>mx){ for (int j=mx+1;j<=i+f[i];j++){ if (!(j&1)){ //printf("%d %d\n",j-i+1,query(rank[(i+i-j)>>1],j-i+1)); ans=max(ans,1ll*(j-i+1)*query(rank[(i+i-j)>>1],j-i+1)); } //printf("ans%d %lld\n",j,ans); } mx=i+f[i],id=i; } } printf("%lld\n",ans); } int main(){ scanf("%s",s+1),n=strlen(s+1); getsa(),geth(),getst(),manacher(); return 0; }