BZOJ 2342 [Shoi2011]双倍回文 Manacher

题目大意:给出一个串,求最长双回文串。其中定义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 1000005
using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值