HDU3068 最长回文(Manacher算法)

来源:互联网 发布:淘宝怎么清空购物车 编辑:程序博客网 时间:2024/04/28 07:13
Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等



Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
 

Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
 

Sample Input
aaaaabab
 

Sample Output
43


这道题的模板来自kuangbin大神,我加上了一点自己的图文解释。

判断回文串最麻烦的一点就是要分奇数和偶数考虑,而Manacher算法就是用一种巧妙的方法,将奇数和偶数放在一起考虑。

具体做法就是在原字符串每两个字符中插入分隔符,首尾也要加上分隔符,防止越界。用Ma[i]记录预处理后的字符串。

用一个数组Mp[i]记录以字符Ma[i]为中心的最长回文字符串的长度。

如下图所示:

如何计算Mp[i]呢?

首先我们先定义几个数。

int mx;//当前已经已经回文串最右字符的最大值

int id;//回文串最右字符的最大值的中心位置

分两种情况考虑。

第一种情况:i<mx

首先找到i关于id的对称位置j(id*2-i),然后还要分两种情况考虑。

1、如果以j为中心的最长回文串在以id为中心的最长回文串的里面,以i为中心的最长回文串就等于以j为中心的最长回文串。

Mp[i]=Mp[j];


2、如果以j为中心的最长回文串超过了以id为中心的最长回文串,那么一开始以i为中心的只能保证黄色部分一定是回文串,最右超过mx部分的只能重新开始匹配。

Mp[i]=mx-i;

我曾经有一瞬间是迷惑不解的,觉得应该是(mx-1)*2,但是我后来想起来处理后的字符串里有“#”啊,所以应该是mx-1。那么重新开始匹配的时候也是匹配了一个字符,就++,而不是+2。


另外,如果新计算的回文串右端点大于mx,就要更新mx和id哦!

第二种情况:

i>=mx

那就老老实实的一个一个重新匹配吧。

以下是完整的代码:

#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>const int MAXN=110010;using namespace std;char s[MAXN];char Ma[MAXN*2];int Mp[MAXN*2];void Manacher(char s[],int len){int l=0;Ma[l++]='$';Ma[l++]='#';for(int i=0;i<len;i++){Ma[l++]=s[i];Ma[l++]='#';}Ma[l]=0;int mx=0,id=0;for(int i=0;i<l;i++){Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;if(i+Mp[i]>mx){mx=i+Mp[i];id=i;}}}int main(void){while(scanf("%s",s)==1){int ans=0;int len=strlen(s);Manacher(s,len);for(int i=0;i<2*len+2;i++)ans=max(ans,Mp[i]-1);printf("%d\n",ans);}return 0;}

0 0
原创粉丝点击