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;
}


你可能感兴趣的:(后缀数组)