前言
距离学习马拉车也有小半年了。但是却学了很多遍,总是学了又忘,学了又忘,要不断的复习才可以记住。自己也对每次都要复习感到繁琐,所以趁今天重新复习马拉车写下一篇小结。如果你在看这篇文章的过程中发现任何问题,欢迎在评论或私信中指出!
马拉车是什么
要学习马拉车,首先要知道它是用来做什么的。manacher是用来解决最长回文子串这系列问题的。他可以用线性的时间轻松解决。
分隔符的运用
我们知道,求最长回文子串的时候有奇数和偶数之分,如果要处理的话分开很麻烦。所以我们就可以进行一下预处理。在每个字符和字符之间插入一个分隔符,我们一般使用’#’号。这样我们就可以将奇数和偶数的放在一起来讨论了。当然,字符串首尾也要放分隔符。
p数组的运用
接下来我们要设一个数组P[i]表示以i为中心的回文子串的半径。那么对于一个字符串
‘aaaba′
‘
a
a
a
b
a
′
,我们就可以这么表示。
manacher算法的具体实现
前面铺垫了这么多,接下来要讲一下manacher具体是怎么实现的。
对于一个
x
x
,我们设为右边最值。设一个
j
j
在的左侧,那么
i=2⋅x−j
i
=
2
·
x
−
j
就是
j
j
关于的对称点。
如图所示:
接下来分情况讨论:
1°
1
°
当
i+p[j]<mx
i
+
p
[
j
]
<
m
x
时,因为他们都是在以
x
x
为中心的回文子串中,所以。
2°
2
°
当
i+p[j]>=mx
i
+
p
[
j
]
>=
m
x
时,因为mx以外的值是无法判断的,所以我们可以先让
p[i]=mx−i
p
[
i
]
=
m
x
−
i
,然后其他的进行暴力解决。
3°
3
°
当
i>mx
i
>
m
x
时,因为
i
i
已经在mx以外了,所以我们只能暴力扫一遍去更新。
这样子我们每一次都去更新x值和mx值,就完成了马拉车算法。
时间复杂度分析
马拉车算法的时间复杂度,因为对于每个点他们的回文子串我们只会算一次,所以他的时间复杂度是线性的。
代码实现
void manacher(void)
{
memset(p,0,sizeof(p));
int x=0,y=0;
for (int i=1;i<=m;i++)
{
if (y>i)
{
if (p[x*2-i]<y-i) p[i]=p[x*2-i];
else p[i]=y-i;
}
else
{
p[i]=1;
}
while (i-p[i]>=1 && i+p[i]<=m && s[i-p[i]]==s[i+p[i]])
{
p[i]++;
}
if (y<i+p[i])
{
x=i;
y=i+p[i];
}
}
}