BZOJ 2342 [Shoi2011]双倍回文(manacher+并查集)

 

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2342

 

【题目大意】

  记Wr为W串的倒置,求最长的形如WWrWWr的串的长度。

 

【题解】

  我们发现要找到这样一个双倍回文,我们可以采取在大的回文串中寻找小的回文串的方式,
  在回文串i中找到回文串j满足j+r[j]>=i那么(i-j)<<1就可以用来更新答案。
  在查找过程中,我们发现在下标小的i中无法被用到的j在下标大的i中也无法被用到,
  因此对于无效的查找区间我们用并查集进行优化

 

【代码】

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=500010;
int n,f[N<<1],r[N<<1],ans;
char s[N],c[N<<1];
void manacher(){
    for(int i=1;i<=n;i++)c[i<<1]=s[i],c[(i<<1)+1]='#';
    c[1]='#';c[n<<1|1]='#';c[0]='&';c[(n+1)<<1]='$';
    int j=0,k; n=n<<1|1;
    for(int i=1;i<=n;){
        while(c[i-j-1]==c[i+j+1])j++;
        r[i]=j;
        for(k=1;k<=j&&r[i]-k!=r[i-k];k++)r[i+k]=min(r[i-k],r[i]-k);
        i+=k;j=max(j-k,0);
    }
}
int sf(int x){return f[x]==x?x:f[x]=sf(f[x]);}
int main(){
    while(~scanf("%d",&n)){
        scanf("%s",s+1); 
		manacher(); ans=0;
        for(int i=1;i<=n;i++)f[i]=(c[i]=='#')?i:(i+1);
        for(int i=3;i<n;i+=2){
            int j=sf(max(i-(r[i]>>1),1));
            for(;j<i&&j+r[j]<i;f[j]=sf(j+1),j=f[j]);
            if(j<i)if((i-j)<<1>ans)ans=(i-j)<<1;
        }printf("%d\n",ans);
    }return 0;
}
posted @ 2017-05-17 17:12  forever97  阅读(160)  评论(0编辑  收藏  举报