您的位置:首页 > 其它

hdu 3068 最长回文 manacher算法

2014-08-10 13:35 302 查看
题意:

给定一个字符串a,找出字符串a中最长的回文子串,输出最长回文子串的长度。

题解:

manacher算法:求字符串最长回文子串长度的算法。详细解释可以看:hdu3068之manacher算法+详解。用图和文字解释的很清楚。

我在这里简要的解释一下:

回文分为两种:偶数长度和奇数长度。为了简便,我们可以将偶数长度的变成奇数长度的。怎么变,在各个字符之间加个不存在的符号,例如#。举例来说adda,可以变成#a#d#d#a#,即变为奇数的回文;同样的奇数长度的加#后还是奇数长度的最长回文。

接着我们定义dp[i]为以第i个字符为中心的回文半径。由于变成了奇数回文,所以回文半径=(回文长度+1)/2;又由于加了#,所以真实回文长度=改变后的回文半径-1。

我们枚举各个字符位置i,若存在一个i,使得i+dp[i]最大,我们用x=i,。然后分情况讨论:

1)i>x+dp[x]时,dp[i]=1,然后用while(a[i+dp[i]]==a[i-dp[i]])dp[i]++;向两边拓展。

2)i<=x+dp[x]时:由于x是回文,所以a[i]=a[2*i-x],之后就看dp[2*i-x]是否等于x+dp[x]-i了,也就是2*i-x的回文半径是否在x的回文半径内。若不相等,我们很容易证明dp[i]=min(dp[2*i-x],x+dp[x]-i);若相等则还需要用while向左右拓展。

在算法过程中,我们用maxv记录最长的回文半径即可。

注意:拓展的时候有可能越界,所以可以加越界判断,或者是两端字符加上不存在在的不同字符。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
using namespace std;

const int maxn=11e4+10;
char a[maxn*2];
int dp[maxn*2];
int Manacher(char *a,int *dp,int n)//求最大回文子串长度
{
    int i,j,k,x=0,maxv=0;
    for(i=2;i<2*n+1;i++)
    {
        if(x+dp[x]>i)dp[i]=min(dp[2*x-i],x+dp[x]-i);
        else dp[i]=1;
        while(a[i-dp[i]]==a[i+dp[i]])dp[i]++;
        if(x+dp[x]<i+dp[i])x=i;
        maxv=max(maxv,dp[i]);
    }
    return maxv-1;
}
int main()
{
    while(scanf("%s",a)!=EOF)
    {
        int i,n;
        n=strlen(a);
        for(i=n;i>=0;i--)
        {
            a[2*i+2]=a[i];
            a[2*i+1]='#';
        }
        a[0]='*';//a[2*n+2]='\0',防止在manacher算法的while循环中溢出
        printf("%d\n",Manacher(a,dp,n));
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: