马拉车
(Manacher)算法是用来(O(n))求解最长回文串长度或输出最长长度的一个由(manacher)发明的一个快速高效的算法。
(Brute-Force)
改题目的暴力算法还是比较好想的,求出该串的所有子串,然后将每个串(O(frac n2))的算法来判断是否回文,这样就可以求出最大回文串的所有子串。
(Manacher)
我们首先要解决暴力解法所不足的地方。
1. 字符串奇偶性问题
我们可以把每两个字符串前都插入一个在给定字符串中肯定不会出现的字符,这样一来无论给定字符串长度是奇是偶,在这一波操作之后就都变成了奇数。且原来是回文的字符串,现在还是回文的。相反原来不是回文串,现在也不是。
inline void init()
{
scanf("%s", c);
int l = strlen(c);
e[++len] = '$';
for (int i = 0; i < l; i++)
{
e[++len] = c[i];
e[++len] = '$';
}
e[++len] = '$';
}
2.时间优化问题
算法的优化时间其实就是相当于减少无用状态的计算。
现在我们定义回文串中最左端与其对称轴的距离称为回文半径,定义数组(hw[i])为以(i)为对称轴的半径.
char: # a # b # a #
hw : 1 2 1 4 1 2 1
hw-1: 0 1 0 3 0 1 0
i : 0 1 2 3 4 5 6
char: # a # b # b # a #
hw : 1 2 1 2 5 2 1 2 1
hw-1: 0 1 0 1 4 1 0 1 0
i : 0 1 2 3 4 5 6 7 8
然后我们发现(hw-1)就是我们要求的答案。
然后问题来了,如何才能更新(hw)数组呢,我们先用一个辅助变量(maxright) 表示已经触及到的最右边的字符,还有(mid)表示包含(maxright)的回文串的对称轴位置。
当(mid<=i<=maxright)时,(i)关于(mid)的对称点j的位置是(mid*2-i),此时(hw[i] = max(hw[i], hw[j])), 然后我们可以继续扩展就可以求出来(hw[i])的确定值了,并且在中途我们还要更新(maxright)和(mid);而如果(i)要大于(maxright)那就是(hw[i])等于(1),然后不停向外扩展。
(Code)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char c[51000100], e[51000100];
int len = -1, hw[51000100], ans;
inline void init()
{
scanf("%s", c);
int l = strlen(c);
e[++len] = '$';
e[++len] = '$';
for (int i = 0; i < l; i++)
{
e[++len] = c[i];
e[++len] = '$';
}
len++;
}
inline void manacher()
{
int maxright = 0, mid;
for (int i = 1; i < len; i++)
{
if (i < maxright)
hw[i] = min(hw[(mid << 1) - i], hw[mid] + mid - i);
else
hw[i] = 1;
while (e[i + hw[i]] == e[i - hw[i]]) ++hw[i];
if (hw[i] + i > maxright)
maxright = hw[i] + i, mid = i;
}
/*
void manacher()
{
int maxright=0,mid;
for(int i=1;i <= n - 1;i++)
{
if(i<maxright)
hw[i]=min(hw[(mid<<1)-i],hw[mid]+mid-i);
else
hw[i]=1;
for(;s[i+hw[i]]==s[i-hw[i]];++hw[i]);
if(hw[i]+i>maxright)
{
maxright=hw[i]+i;
mid=i;
}
}
}
*/
}
int main()
{
init();
manacher();
for (int i = 0; i < len; i++)
ans = max(ans, hw[i]);
printf("%d", ans - 1);
}