Manacher算法
Manacher算法主要是用来解决字符串回文串的问题的。
Manacher算法把长度为奇数和偶数的字符串一起考虑
算法流程
假设我们现在要求出字符串
s
s
的最长回文子串.
我们先对原串进行处理,把长度为奇数的和偶数的放在一起考虑。
其实就是在每两个字符串中间加入一个特殊的字符(只要是没有在原串中出现过的就可以,一般是#)
int Init()
{
int len=strlen(s);
int j=2;
T[0]='$';
T[1]='#';
for(int i=0;i<len;i++)
{
T[j++]=s[i];
T[j++]='#';
}
T[j]='\0';
return j;
}
其中是经过处理后的字符串的长度.
然后我们就可以开始对处理得到的字符串
T
T
进行处理。
Manacher算法还要求一个辅助数,
Len[i]
L
e
n
[
i
]
表示以字符
T[i]
T
[
i
]
为中心的最长回文字串的最右字符到
T[i]
T
[
i
]
的长度,比如以
T[i]
T
[
i
]
为中心的最长回文字串是
T[l,r]
T
[
l
,
r
]
,那么
Len[i]=r−i+1
L
e
n
[
i
]
=
r
−
i
+
1
。
显然对于
Len[i]
L
e
n
[
i
]
,我们有一个非常好的性质:
那就是
Len[i]−1
L
e
n
[
i
]
−
1
就是以
i
i
为中心的原串的最长回文子串的长度,(我们不是在原串中每隔两个字符又加了一个字符么)
既然有这么好的性质,那我们计算出
Len[]
L
e
n
[
]
数组就可以求出最长回文子串的长度了。
怎样求
Len[]
L
e
n
[
]
呢?
我们从左向右开始计算。
我们设
id
i
d
表示当前得到的最长回文子串的中心,
mx
m
x
为当前得到的最长回文子串的右端点。
对于
Len
L
e
n
数组的计算分为两种情况:
一.当前要计算的
i
i
点小于.
那么对于
i
i
的最长回文子串只有两种情况:一种是关于id的对称点
j
j
的最长回文子串的长度,另一种是的长度。
对于第一种情况:
显然因为
mx‘−mx
m
x
‘
−
m
x
是以
id
i
d
为中心的最长回文子串,那么以
i
i
为中心的最长回文子串的长度一定等于,关于
id
i
d
的对称点
j
j
的最长回文子串的长度。
对于第二种情况:
为什么还要呢?
这是因为
mx‘
m
x
‘
左边和
mx
m
x
右边的字符是不一样的,有可能存在这样一种情况:
二.当前要计算的
i
i
点大于.
那么只能一位一位的计算了:
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int MAXN=11000300;
using namespace std;
int p[MAXN<<1];
char T[MAXN<<1],s[MAXN<<1];
int Init()
{
int len=strlen(s);
int j=2;
T[0]='$';
T[1]='#';
for(int i=0;i<len;i++)
{
T[j++]=s[i];
T[j++]='#';
}
T[j]='\0';
return j;
}
int Manacher()
{
int len=Init();
int maxx=-1;
int mx=0,id;
for(int i=1;i<len;i++)
{
if(i<mx)
p[i]=min(mx-i,p[2*id-i]);
else
p[i]=1;
while(T[i+p[i]]==T[i-p[i]])
p[i]++;
if(i+p[i]>mx)
{
id=i;
mx=i+p[i];
}
maxx=max(maxx,p[i]-1);
}
return maxx;
}
int main()
{
scanf("%s",s);
printf("%d",Manacher());
return 0;
}