Manacher算法,又叫“马拉车”,它可以在时间复杂度和空间复杂度都是O(n)的情况下,求出一个字符串的最长回文串长度。
算法思路
首先回文串分为奇回文串(ababa)和偶回文串(abba),因为偶回文串是不对称的,比较难处理
对于奇偶字符串的处理,manacher采用的是填充特殊字符的方法
将奇偶字符串都变成奇字符串(ababa) >> (@#a#b#a#b#a#),(abba)>>(@#a#b#b#a#)
在字符串中间加#,开头加@(‘#’、‘@’一般不会出现,所以使用它们)
重定义字符串的长度为2*strlen(s)+1(@只是防止越界的字符)
int getstr()
{
int k = 0;
str[k++] = '@';//开头加个特殊字符防止越界
int len = strlen(s);
for (int i = 0; i < len; i++) {
str[k++] = '#';
str[k++] = s[i];
}
str[k++] = '#';
str[k] = '\0'; // 防止越界
return k; // 新字符串的长度
}
对重定义的字符串进行Manacher算法
int Manacher()
{
int ans = 0;
int mx = 0,id = 0;
int len = getstr();
for (int i = 1; i < len; i++){
if (mx > i) {
Len[i] = min(mx - i,Len[2 * id - i]);
}
else Len[i] = 1;
while(str[i + Len[i]] == str[i - Len[i]]) Len[i]++;
if (Len[i] + i > mx){
mx = Len[i] + i;
id = i;
ans = max(ans,Len[i]);
}
}
return ans - 1;
}
在该算法中,mx就是以id为中心的最长回文右边界,Len[i]代表以i为中心的最长回文串长度
(mx = id + Len[id])
其他代码都挺好理解的,就是这个代码为什么这样更新呢
if (mx > i) {
Len[i] = min(mx - i,Len[2 * id - i]);
}
首先2 * id - i是i关于id的对称点,所以Len[i]的长度就等于Len[2 * id - i]的长度(也不能说是完全相等,应该是在这个id区间内对称相等,所以不能超过mx这个边界,要取个min)
完整模板代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int M = 1e4 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
typedef long long ll;
char str[N];
char s[N];
int Len[N];
int getstr()
{
int k = 0;
str[k++] = '@';//开头加个特殊字符防止越界
int len = strlen(s);
for (int i = 0; i < len; i++) {
str[k++] = '#';
str[k++] = s[i];
}
str[k++] = '#';
str[k] = '\0'; // 防止越界
return k; // 新字符串的长度
}
int Manacher()
{
int ans = 0;
int mx = 0,id = 0;
int len = getstr();
for (int i = 1; i < len; i++){
if (mx > i) {
Len[i] = min(mx - i,Len[2 * id - i]);
}
else Len[i] = 1;
while(str[i + Len[i]] == str[i - Len[i]]) Len[i]++;
if (Len[i] + i > mx){
mx = Len[i] + i;
id = i;
ans = max(ans,Len[i]);
}
}
return ans - 1;
}
int main()
{
while(scanf("%s",s) != EOF){
printf("%d\n",Manacher());
}
return 0;
}