问题概述:输入一个字符串,输出它的最长回文子串
输入样例: 对应输出:
abbaabcba 5
manacher算法步骤:
①处理字符串,使所有字母左右各有一个符号"#",且只有一个,第一个字符(str[0])设为"&"最后一个字符后面改'\0'
例如:abbaabcba处理后为&#a#b#b#a#a#b#c#b#a#'\0'
目的:将奇数/偶数长度的回文子串全部转换成奇数长度
②设一个数组P[i]来记录以字符Str[i]为中心的最长回文子串向左/右扩张的长度(回文字符串长度半径,最小为1),
其中P[0]=0
例如: & # a # b # b # a # a # b # c # b # a # \0
对应P[i]:0 1 2 1 2 5 2 1 2 5 2 1 2 1 6 1 2 1 2 1
③再增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id]也就是最大回文子串的边界
④从第一个字符("#")开始循环,用while(str[i+p[i]]==str[i-p[i]]) p[i]++判断以i为中心回文半径,
当然如果mx>i,那么P[i]一定>=min(P[2*id-i], mx-i),直接初始化P[i]=min(P[2*id-i], mx-i)再循环以省时间
原理:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824/
复杂度:O(n)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int p[200010];
char s[100010], str[200010];
int main(void)
{
int n, i, k, ans, mx, id;
while(scanf("%s", s)!=EOF)
{
n = strlen(s);
memset(str, '\0', sizeof(str));
str[0] = '$';
str[1] = '#';
for(i=0;i<=n-1;i++) /*步骤1*/
{
k = (i+1)*2;
str[k] = s[i];
str[k+1] = '#';
}
n = k+1;
str[n+1] = 0;
mx = ans = 0;
for(i=1;i<=n;i++)
{
p[i] = 1;
if(mx>i)
p[i] = min(p[2*id-i], mx-i+1);
while(str[i+p[i]]==str[i-p[i]])
p[i]++;
if(p[i]+i-1>mx)
{
mx = p[i]+i-1;
id = i;
}
ans = max(ans, p[i]);
}
printf("%d\n", ans-1);
}
return 0;
}