P3805 【模板】manacher算法

题目描述

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.

字符串长度为n

输入输出格式

输入格式:

 

一行小写英文字符a,b,c...y,z组成的字符串S

 

输出格式:

 

一个整数表示答案

 

输入输出样例

输入样例#1: 
aaa
输出样例#1: 
3

说明

字符串长度len <= 11000000

 

  记录p[i]表示i能向两边推(包括i)的最大距离,如果能求出p,则答案就是max(p)-1了(以i为中点的最长回文为2*p[i]-1,但这是加过字符后的答案,把加进去的字符干掉,最长回文就是p[i]-1)。假设当前能达到的最右边为R,对应的中点为pos,j是i的对称点。 


1.当i<R时 

 

由于L~R是回文,所以p[i]=p[j](i的最长回文和j的最长回文相同)。 
 

这种情况是另一种:j的最长回文跳出L了。那么i的最长回文就不一定是j的最长回文了,但蓝色的肯定还是满足的。

综上所述,p[i]=min(p[2*pos-i],R-i)。 


2.当i>=R时 
由于后面是未知的,于是只能暴力处理了。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int N=22000010;

int L;
char a[N>>1],s[N];
int mid,maxr;
int len[N];

void init()
{
    scanf("%s",a);
    int l=strlen(a);
    for(int i=0;i<l;++i)
        s[++L]='#',
        s[++L]=a[i];
    s[0]='@';
    s[++L]='#';
}

void manacher()
{
    for(int i=1;i<=L;++i)
    {
        if(maxr>i)        //i在右端点左边 
            len[i]=min(len[mid*2-i],maxr-i);  //取与对称点回文长度和i->maxr的最小值 
        else    //i在maxr右边 
            len[i]=1;
        for(;s[i+len[i]]==s[i-len[i]];++len[i]);
        if(i+len[i]>maxr)
        {
            maxr=i+len[i];
            mid=i;
        }
    }
}

int main()
{
    init();
    manacher();
    int ans=*max_element(len+1,len+L+1);
    printf("%d",ans-1);        //多算了一次i,要减去1 
    return 0;
}

 

posted @ 2018-02-10 21:20  whymhe  阅读(128)  评论(0编辑  收藏  举报