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

浙公网安备 33010602011771号