bzoj 2565 manacher

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
 

Data Constraint

 
 

Hint

【样例说明】


从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。

 

【数据范围】

    对于10%的数据,2≤|S|≤103。

  对于30%的数据,2≤|S|≤104。

  对于100%的数据,2≤|S|≤105。

思路:

先做一遍MANACHER,然后枚举分界点(为#)

那么以这个点为分界的最长双回文串会由最左边的能覆盖到这个点的串和最右边的能覆盖到这个点的串组成

那么扫两遍求出每个点最左和最右能覆盖到它的回文串中心。(就是反过来求以每个点为中心的回文串能覆盖哪些点

然后枚举即可

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;

char s[200011];
int r[200011],lm[200011],rm[200011];
int i,n,x,z,q,len,j;
char c;

void Read()
{
    while(c=getchar(),c<'a'||c>'z');
    s[++n]='#';
    s[++n]=c;
    while(c=getchar(),c>='a'&&c<='z'){
        s[++n]='#';
        s[++n]=c;
    }
    s[++n]='#';
}

void Manacher()
{
    int i,len,l,k;
    len=0;
    for(i=1;i<=n;i++){
        if(len<i){
            l=0;
            while(s[i-l]==s[i+l]&&i-l>=1&&i+l<=n)l++;
            r[i]=l;
        }
        else{
            l=min(len-i+1,r[k+k-i]);
            while(s[i+l]==s[i-l]&&i-l>=1&&i+l<=n)l++;
            r[i]=l;
        }
        if(i+r[i]-1>len){
            len=i+r[i]-1;
            k=i;
        }
    }
}

int main()
{
    Read();
    Manacher();
    r[0]=0;
    len=0;
    for(i=1;i<=n;i++){
        if(i+r[i]-1>len){
            for(j=len+1;j<=i+r[i]-1;j++)lm[j]=i;
            len=i+r[i]-1;
        }
        if(len==n)break;
    }
    len=n+1;
    for(i=n;i>=1;i--){
        if(i-r[i]+1<len){
            for(j=i-r[i]+1;j<=len-1;j++)rm[j]=i;
            len=i-r[i]+1;
        }
        if(len==1)break;
    }
    for(i=1;i<=n;i++)if(s[i]=='#'){
        z=(rm[i]-i+1)*2-1+(i-lm[i]+1)*2-1-1;
        if(z/2>q)q=z/2;
    }
    printf("%d
",q);
}
原文地址:https://www.cnblogs.com/applejxt/p/3813454.html