和KMP算法一样,Manacher算法也是一个O(n)的算法,可以在低复杂度的情况下判断最长的回文串长度。
讲解这个算法之前,大家可以回顾一下KMP算法,KMP算法的应用是用到了一个next数组进行跳转来,其原理就是如果当我们匹配到某一位不再相同的时候那么前面的就能确定(有关这个不懂得可以去看小编的KMP算法讲解),充分应用这个信息可以省去很多不必要的匹配步骤。(同时感谢qyq同学提出的小编在讲解细节上的问题)
Manacher算法也是一样,对于回文串的判断很容易想到是找一个中心,再向两边扩散,但是我们真的需要对所有的点都进行向两边扩散找最大回文的操作吗,答案当然是否定的,因为回文串的性质是两边都是对称的,所以如果我们当前的这个点在之前的匹配中出现过,那么这个点目前的情况肯定是关于之前的某一个点对称的点相同。这可能听起来很抽象,但其实确实是一个非常好懂的原理。我们设当前能匹配到最远的那个中心点为id,匹配的一边的长度为Max,p[i]来表示点i 的匹配长度,那么当前能匹配到最远的点就达到了id+Max,加入此时我们正在对点j进行匹配,如果j < id+Max,如图(图是小编我亲手绘制的,望大家别嫌弃):
__-____________-_____________-________
j' id j
那么必定会有一个j' ( 2*id-j )关于j 点对称,由于回文串是对称的,那么j 点的情况肯定是和j' 是一样的,那么j 就可以从min(p[2*id - j], id + Max - j) 的匹配长度开始向两边扩散找最大的匹配长度,这种和KMP算法想死运用到之前的信息的方法在匹配的过程中肯定是省下了很多不必要的匹配过程,大大的增加了匹配的效率。
接下来小编给出此算法的代码,当然核心就是刚刚已经给出的min(p[2*id - j], id + Max - j)这段:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1000005;
char str[maxn];
char initstr[maxn];
int p[maxn];
int init()
{
initstr[0] = '$';
int num = 1;
int len = strlen(str);
for(int i=0; i<len; i++)
{
initstr[num++] = '#';
initstr[num++] = str[i];
}
initstr[num++] = '#';
initstr[num] = '\0';
return num;
}
int manacher(int n)
{
int Max = 0;
int id;
int maxnum = -1;
for(int i=0; i<n; i++)
{
if(Max > i)
p[i] = min(p[2*id-i],Max-i);
else p[i] = 1;
while(initstr[i-p[i]] == initstr[i+p[i]]) p[i]++;
if(Max < i+p[i])
{
Max = i+p[i];
id = i;
}
maxnum = max(p[i], maxnum);
}
return maxnum;
}
int main()
{
while(scanf("%s",str)!=EOF)
{
int n = init();
printf("%d\n",manacher(n)-1);
}
return 0;
}