Manacher(马拉车)算法
第一篇博客,记录算法学习
马拉车算法是一种可以用线性复杂度解决求最长回文子串的算法,下面将具体介绍该算法的思想与实现。
暴力算法
通过暴力枚举每个子串(
我们也可以遍历字符串中每个字符(
Manacher(马拉车)算法
1.使用一个
2.利用已求得的
算法介绍
首先,回文串可以分为奇数长度的回文串(如 “abcba” )和偶数长度的回文串(如 “abba” )。在奇回文串中,回文串的对称中心是一个具体的字符(如 “abcba” 中的 “c” )。而在偶回文串中,回文串的对称中心是两个字符中间的间隔,不再是一个具体的字符。显然,这会影响到我们对称中心的选择。
所以我们可以通过在字符间增加间隔符,来人为地使所有回文串变成奇回文串,如下:
我们在字符串 “abcba” 的前后以及每个字符的中间添加字符“#”,得到
abcba -> #a#b#c#b#a#
同时我们也可以在字符串的前后添加开始和结束符号(这里使用 “[” 与 “]” ),这样也可以避免数组边界的判断,美化代码。
#a#b#c#b#a#->[#a#b#c#b#a#]
通过以上操作,我们使得字符串中仅存在奇回文串了,接下来开始正式介绍马拉车算法。
首先我们需要
1.i>R
在这种情况中,我们无法利用已知的最长回文串获得任何与
2.i<=R
此时,
通过遍历字符串,我们只需维护最大的
完整代码
int Manacher(string str){
vector<int> ans;
int len=str.length(),l=1,r=1,MAX=0;
//添加间隔符,令字符串只存在奇长度回文子串
str.resize(2*len+3),ans.resize(2*len+3,1);
for(int i=2*len+2;i>=1;i--){
str[i]=i&1?'#':str[i/2-1];
}
str[0]='[',str[2*len+2]=']';
//计算以每个字符中心得到的最长回文字串
for(int i=1;i<=2*len+2;i++){
//根据回文的对称性,避免重复计算
if(i<=r){
ans[i]=min(ans[l+r-i],r-i+1);
}
//扩张未探索区域
while(str[i-ans[i]]==str[i+ans[i]]){
ans[i]++;
}
//更新最长回文串
if(i+ans[i]>=r){
l=i-ans[i]+1,r=i+ans[i]-1;
}
MAX=max(MAX,ans[i]-1);
}
return MAX;
}
【模板】manacher 算法