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; }
愿你出走半生,归来仍是少年