Manacher算法,又叫“马拉车”算法
可以在时间复杂度为O(n)的情况下求解一个字符串的最长回文子串长度的问题。
马拉车算法
回文分为偶回文(abba)和奇回文(abcba)。在处理奇偶回文之间有差异,所以用到了一个技巧
在 (每个字符之间和开头) 插入一个无关的字符,
比如说: abbahopxpo 转换为 $#a#b#b#a#h#o#p#x#p#o# ( ’ $ ’ 是防止越界 )
起初有一个偶回文abba和一个奇回文opxpo,被转换为#a#b#b#a#和#o#p#x#p#o#,长度都转换成了奇数。
回文半径数组radius
回文半径数组radius是用来记录以每个位置的字符为回文中心求出的回文半径长度,如下图所示
可以看出,radius[i] - 1正好是原字符串中最长回文串的长度。
求解回文半径数组radius
mx 代表以 id 为中心的最长回文的右边界,也就是mx = id + radius[id]。
如图,mx 到 mx 关于 id 对称点之间是一个回文串,
要是从 j 到 mx 的对称点 之间存在回文串,那么 i 到 mx 之间也一定会存在回文串
那么以 i 为对称点的回文串的长度最少是
m
i
n
(
r
a
d
i
u
s
[
j
]
,
m
x
−
i
)
min(radius[j], mx-i )
min(radius[j],mx−i)
并且
j
=
2
∗
i
d
−
i
j= 2 * id - i
j=2∗id−i
所以最终优化的是
r
a
d
i
u
s
[
i
]
=
m
i
n
(
r
a
d
i
u
s
[
2
∗
i
d
−
i
]
,
m
x
−
i
)
radius[i] = min(radius[2 * id - i], mx - i)
radius[i]=min(radius[2∗id−i],mx−i)
code
/*
最长回文
HDU - 3068
https://cn.vjudge.net/problem/HDU-3068
题面:最长回文长度
解法:马拉车算法
*/
#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
int radius[maxn];
char s[maxn];
char s_new[maxn];
int init(){
int len=strlen(s);
s_new[0]='$';
s_new[1]='#';
int j=2;
for(int i=0;i<len;i++){
s_new[j++]=s[i];
s_new[j++]='#';
}
s_new[j]='\0';
return j;
}
int Manacher(){
int len=init();
int ans=-1;
int id;
int mx=0;
for(int i=1;i<len;i++){
if(i<mx) radius[i]=min(radius[2 * id - i], mx - i);
else radius[i]=1;
while(s_new[i-radius[i]]==s_new[i+radius[i]]){
radius[i]++;
}
if(mx<i+radius[i]){
id=i;
mx=i+radius[i];
}
ans=max(ans,radius[i]-1);
}
return ans;
}
int main(){
while(~scanf("%s", s)){
printf("%d\n", Manacher());
}
}
参考:Manacher算法
参考:Manacher算法的详细讲解