m a n a c h e r 算 法 \color{green}{manacher算法} manacher算法
【
例
题
】
:
\color{blue}{【例题】:}
【例题】: 给出一个只由小写英文字符a,b,c...y,z
组成的字符串
S
S
S,求
S
S
S中最长回文串的长度(见洛谷P3805
)。
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 我们先考虑暴力算法:枚举一个回文中心,然后向两边扩展。
这样做有两个问题:一是需要区分奇数和偶数。比如aba
和abba
,我们肉眼一看就知道两个都是回文,但是对于偶数回文串而言,它们的回文中心不是一个可见字符(比如abba
的回文中心在两个b
之间)。二是时间复杂度太高。枚举的时间复杂度为
O
(
∣
S
∣
)
O(|S|)
O(∣S∣)(
∣
S
∣
|S|
∣S∣表示字符串
S
S
S的长度)。而拓展的时间复杂度最坏为
O
(
∣
S
∣
)
O(|S|)
O(∣S∣),总的时间复杂度为
O
(
∣
S
∣
2
)
O(|S|^2)
O(∣S∣2)。
m
a
n
a
c
h
e
r
算
法
(
中
文
名
:
马
拉
车
算
法
)
\color{red}{manacher算法(中文名:马拉车算法)}
manacher算法(中文名:马拉车算法),是解决此问题的
O
(
∣
S
∣
)
O(|S|)
O(∣S∣)算法。首先,为了统一奇偶情况,我们在每个字符之间加上一个没有在
S
S
S中出现的字符(比如aba
变成$a$b$a$
)。
记 p [ i ] p[i] p[i]表示以第 i i i个字符为回文中心的最长回文半径, r r r为最长回文右边界, m i d mid mid为最长回文右边界中心, i i i表示当前求解 p [ i ] p[i] p[i]。
考虑如何求 p p p数组,求解有好几种情况:
- 当 i i i在 r r r左边且在 m i d mid mid右边时, i i i关于 m i d mid mid的对称点为 m i d × 2 − i mid \times 2-i mid×2−i,我们令 p [ i ] = p [ j ] p[i]=p[j] p[i]=p[j],然后从 i + p [ i ] i+p[i] i+p[i]开始拓展求解。
- 当 i i i在 r r r右边时,我们就按原始方法进行计算。
【 代 码 】 : \color{blue}{【代码】:} 【代码】:
const int N=11001000;
char s[N],s1[N<<1];
int p[N<<1],ans,n;
inline void manacher(){
s1[0]='~';s1[1]='$';
for(int i=1;i<=n;i++){
s1[i*2]=s[i];
s1[i*2+1]='$';
}
}
inline void calc_answer(){
for(int t=1,r=0,mid=0;t<=2*n+1;t++){
if (t<=r) p[t]=min(p[mid*2-t],r-t+1);
while (s1[t-p[t]]==s1[t+p[t]]) ++p[t];
if (p[t]+t>r) r=p[t]+t-1,mid=t;
if (p[t]>ans) ans=p[t];
}
}
int main(){
// freopen("t1.in","r",stdin);
scanf("%s",s+1);
n=strlen(s+1);
manacher();
calc_answer();
printf("%d",ans-1);
return 0;
}