BZOJ 2565 Manacher 解题报告

2565: 最长双回文串

Description

顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。

Input

一行由小写英文字母组成的字符串S。

Output

一行一个整数,表示最长双回文子串的长度。

Sample Input

baacaabbacabb

Sample Output

12

【解题报告】
首先可以相当manacher算法当中有这样一个过程, 用两个辅助变量mx和p分别表示已有回文半径覆盖到的最右边界和对应中心的位置, 然后在求解过程中更新mx和p, 考虑到S和T的中间字符作为分隔的位置, 当这个位置被mx覆盖之后, mx从左往右更新第一次覆盖的时候p(覆盖它的串的中心)的位置一定是离这个位置最远的也就是找到了S的中心, 由于mx移动距离至多为n, 所以随着manacher算法的进行用dp来记录mx被更新时的那个回文串的信息即可, 找到S中心之后, 类似的反向从右往左做一遍manacher然后找到T的中心
这样O(n)的预处理之后找出了每个分隔符之下S和T的中心的最佳位置, 最后枚举一下分隔符的位置即可找到最优解
总体时间复杂度O(n)

代码如下:

/**************************************************************
    Problem: 2565
    User: onepointo
    Language: C++
    Result: Accepted
    Time:80 ms
    Memory:14492 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
#define N 1000010

char s1[N],str[N];
int n,len,rl[N],mx,pos,ls[N],rs[N],ans;

int main()
{
    scanf("%s",s1);
    int i,j,a;
    len=strlen(s1);
    for(i=0;i<len;i++) str[n++]='*',str[n++]=s1[i];
    str[n++]='*';
    for(mx=-1,i=0;i<n;i++)
    {
        if(mx>i) rl[i]=min(mx-i+1,rl[2*pos-i]);
        else rl[i]=1;
        for(;i+rl[i]<n&&rl[i]<=i&&str[i+rl[i]]==str[i-rl[i]];rl[i]++);
        if(i+rl[i]-1>mx) mx=i+rl[i]-1,pos=i;
        rs[i-rl[i]+1]=max(rs[i-rl[i]+1],rl[i]-1);
        ls[i+rl[i]-1]=max(ls[i+rl[i]-1],rl[i]-1);
    }
    for(i=0;i<n;i+=2) rs[i]=max(rs[i],rs[i-2]-2);
    for(i=n-1;i>=0;i-=2) ls[i]=max(ls[i],ls[i+2]-2);
    for(i=0;i<n;i+=2) ans=max(ans,ls[i]+rs[i]);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值