zoukankan      html  css  js  c++  java
  • 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]='';
    	//printf("%s
    ",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
    ",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
    ",j,ans);
    			}
    			mx=i+f[i],id=i;
    		}
    	}
    	printf("%lld
    ",ans);
    }
    
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	getsa(),geth(),getst(),manacher();
    	return 0;
    }


  • 相关阅读:
    49.把字符串转发为整数(python)
    48.不用加减乘除做加法(python)
    47.1+2+3+...+n(python)
    46.孩子们的游戏(python)
    45.扑克牌顺子(python)
    44.翻转单词序列(python)
    43.左旋转字符串(python)
    42.和为S的两个数字(python)
    41.和为S的连续整数序列(python)
    39.平衡二叉树(python)
  • 原文地址:https://www.cnblogs.com/thythy/p/5493536.html
Copyright © 2011-2022 走看看