Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
回文就是正反读都是一样的字符串,如aba, abba等
Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
Sample Input
aaaa abab
Sample Output
4 3
这道题的模板来自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;
}