Mannacher 算法
马拉车算法(中文翻译,有点搞笑)主要是用来求解字符串的最大回文字串。回文串是一个很常出现的东西,求解字符串的回文串也是很经典的题目,马拉车算法是一个可以在 O ( n ) O(n) O(n)复杂度内求解字符串的最大回文串的算法。
基本介绍
在求解前我们要先对该字符串做一些预处理,其过程为: 在字符串的最前面加上"@#", 当然这两个符号都不能出现字符串当中,之后在字符串每个字符后面加上一个"#"; 举个例子,字符串"abcba"处理之后变成了"@#a#b#c#b#a#"; 这样做的目的是为了方便后面的讨论(不用分奇偶讨论);
该算法需要计算一个数组
p
p
p, 表示以每个位置为中心的字符的最大回文半径。两个辅助变量
m
x
mx
mx和
i
d
id
id, 分别表示以
i
d
id
id为中心回文串的最大边界
m
x
mx
mx。
算法的核心步骤:
p
[
i
]
=
m
x
>
i
?
m
i
n
(
p
[
2
⋅
i
d
−
i
]
,
m
x
−
i
)
:
1
p[i]=mx>i? min(p[2\cdot id-i], mx-i):1
p[i]=mx>i?min(p[2⋅id−i],mx−i):1
这个公式的意思为
m
x
>
i
mx>i
mx>i时,
p
[
i
]
=
m
i
n
(
p
[
2
⋅
i
d
−
i
]
,
m
x
−
i
)
p[i]=min(p[2\cdot id -i], mx-i)
p[i]=min(p[2⋅id−i],mx−i), 否则
p
[
i
]
=
1
p[i]=1
p[i]=1
推导过程:
记
j
j
j是i关于
i
d
id
id对称的点,则
j
=
2
⋅
i
d
−
i
j=2\cdot id-i
j=2⋅id−i. 此时以
i
d
id
id为对称的中心的最大的半径为
m
x
mx
mx(也是当前回文串能延伸的最远处).
(1) 当
m
x
≤
i
mx \leq i
mx≤i 时
此时显然对
i
i
i单独考虑,一个一个去匹配。
(2)
m
x
>
i
mx>i
mx>i
(
i
)
(i)
(i) 当
m
x
−
i
>
p
[
j
]
mx-i>p[j]
mx−i>p[j] 时
此时以
p
[
j
]
p[j]
p[j]为中心的最大回文串在以
i
d
id
id为中心的回文串内,又因为回文串的对称性,所以
p
[
i
]
=
p
[
j
]
=
p
[
2
⋅
i
d
−
i
]
p[i]=p[j]=p[2\cdot id-i]
p[i]=p[j]=p[2⋅id−i].
(
i
i
)
(ii)
(ii) 当
m
x
−
i
≤
p
[
j
]
mx-i \leq p[j]
mx−i≤p[j]时
此时以
p
[
j
]
p[j]
p[j]为中心的最大回文串半径大于等于
m
x
−
i
mx-i
mx−i, 而在
m
x
mx
mx之外的则需要单独寻找.
代码:
// 小学生一发的刷题之路
//
// 最大回文串-马拉车算法
//
//
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#include <deque> //双向队列;
#include <cmath>
#include <set>
#include <stack>
#include <map>
#include <vector>
#include <cstdlib>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double PI=acos(-1.0);
const double eps=1e-8;
const int maxn=1e5+5;
const int maxm=1e3+5;
const ll mod=1e9+7;
const int INF=1e8;
template<class T>
inline void read(T &ret){ //快速输入模版;
ret=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ret=ret*10+c-'0';
c=getchar();
}
ret*=f;
}
template <class T>
inline void out(T ret){ //快速输出模版;
if(ret>9)
{
out(ret/10);
}
putchar(ret%10+'0');
}
string str;
int p[maxn];
void Manacher(string str){
//字符串的预处理;
string new_str="s#";
for(int i=0;i<str.size();i++){
new_str.push_back(str[i]);
new_str+="#";
}
memset(p,0,sizeof(p));
int mx=0,id=0,resLen=0,resCenter=0;
for(int i=1;i<new_str.size();i++){
p[i]=mx>i?min(p[2*id-i],mx-i):1;
while(new_str[i+p[i]]==new_str[i-p[i]]){ //匹配相等的;
p[i]++;
}
if(mx<i+p[i]){
mx=i+p[i];
id=i;
}
if(resLen<p[i]){
resLen=p[i];
resCenter=i;
}
}
//最大回文串的开头位置为新字符串的中心位置减去半径长/2;
//回文串的长度为半径长减1;
int maxLen=resLen-1,st=(resCenter-resLen)/2;
for(int i=st;i<st+resLen-1;i++){
cout<<str[i];
}
cout<<endl;
}
int main()
{
cin>>str;
Manacher(str); //马拉车算法求最大回文字串;
return 0;
}
新的开始,每天都要快乐哈!