BZOJ 2342 [Shoi2011]双倍回文 Manacher

来源:互联网 发布:广播剧后期制作软件 编辑:程序博客网 时间:2024/05/18 01:22

题目大意:给出一个串,求最长双回文串。其中定义s’为s的镜像串,则s+s’是一个回文串,当串ss=s+s’+s+s’时称ss为双回文串。

首先想到用manacher处理出每个字符处的最长回文半径。

由于不可能存在两个奇数长度的回文串拼在一起的双回文串,所以只有以’#’为中心的回文半径是有用的。

记f(i)为以串s中的位置i的后面的位置为对称轴,最长的回文半径。对于一个对称轴x,我们要找到一个最长的串s用len(s)*4来更新答案。也就是找到一个距离x最远的位置y,其中y-f(y)<=x && x+f(x)/2<=y,用(y-x)来更新答案。

那么枚举位置x,要找第一个不大于(x+f(x)/2)的位置可以维护一个set,将每一个位置上i-f(i)排序,这样单调地插入能保证始终满足第一个条件,注意细节。

#include <cstdio>#include <set>#include <algorithm>#define N 1000005using namespace std;struct Data {    int ord,val;    Data(int _=0,int __=0):ord(_),val(__) {}    bool operator < (const Data& rhs) const { return val<rhs.val; }}a[N];int n,len,pos,Maxright,RL[N],f[N/2];char s[N];set<int> S;int main() {    scanf("%d%s",&n,s);    len=n*2+1;    for(int i=n-1;~i;i--) s[i*2+1]=s[i], s[i*2]='#';    s[n*2]='#';    for(int i=0;i<len;i++) {        if(i<Maxright) RL[i]=min(RL[pos*2-i],Maxright-i);        else RL[i]=1;        while(i+RL[i]<len && i-RL[i]>=0 && s[i+RL[i]]==s[i-RL[i]]) RL[i]++;        if(i+RL[i]-1>Maxright) Maxright=i+RL[i]-1, pos=i;    }    for(int i=0;i<n;i++) f[i]=(RL[i*2]-1)/2;    for(int i=0;i<n;i++) a[i]=Data(i,i-f[i]);    sort(a,a+n);    int ans=0;    int j=0;    for(int i=0;i<n;i++) {        while(j<n && a[j].ord-f[a[j].ord]<=i) S.insert(a[j++].ord);        set<int> :: iterator it=S.upper_bound(i+f[i]/2);        if(it!=S.begin()) ans=max(ans,(*--it-i)*4);    }    printf("%d\n",ans);    return 0;}
0 0
原创粉丝点击