最近有个朋友给我介绍tx的面经时提到了最长回文子串问题,这才知道原来还有时间复杂度为O(n)的解法——Manacher算法,于是去翻了翻博客,发现对于半径数组p的讲解都非常难懂,为了节约以后自己复习算法的时间,特地写一篇博客来好好理解这一部分的内容。在浏览本文前,请确保自己已了解O(n2)的算法,因为Manacher算法本质上是对于O(n2)算法的一种优化。
先给出我学习这个算法时主要参考的博客:https://blog.youkuaiyun.com/happyrocking/article/details/82622881
一、两个辅助指针
在计算半径数组p时,一般会涉及到两个指针:id和mx,这两个指针是根据已发现的右边界位置最大的回文子串来定义的(请区分实际的最长回文子串和”已发现的右边界位置最大的回文子串“),其中id是上述回文子串的中心位置,mx是上述回文子串的右边界位置(也就是末尾的位置)。
举个例子:
假设字符串s="#g#o#o#g#l#e#"
| 位置i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| s[i] | # | g | # | o | # | o | # | g | # | l | # | e | # |
和O(n^2)的算法类似,我们每次以位置i为中心寻找最长回文子串。
当i=4时,已发现的右边界位置最大的回文子串是“#o#”,我们可以更新id=4,mx=5。
当i=5时,已发现的右边界位置最大的回文子串是“#g#o#o#g#”,我们更新id=5,mx=9。
二、利用回文串特性降低时间复杂度
可能有人会有疑问:既然还是要像O(n2)的算法一样以位置i为中心寻找最长回文子串,那是怎么做到把时间复杂度降低为O(n)的呢?其关键就在于mx——当i<mx时,我们利用回文串的特性来避免“以位置i为中心寻找最长回文子串”这一操作。如果i<mx时每次操作的复杂度是O(1),那么一旦mx为原字符串最末端,那么之后的总操作复杂度最多也就是O(n);而mx扩展到最末端所需的时间复杂度最多也是O(n),所以整个算法的时间复杂度就是O(n)。
以上说法只是为了便于理解,非常不严谨,真正的推导过程请自寻查找。
那么接下来的重点就很明显了:当i<mx时,如何利用回文串的特性来避免“以位置i为中心寻找最长回文子串”这一操作呢?很多博客都是用下面这张图来讲的(包括我主要参考的那篇):

本文深入解析Manacher算法,一种求解最长回文子串问题的高效算法,时间复杂度为O(n)。文章通过对比O(n^2)的传统算法,详细阐述了Manacher算法的原理,包括辅助指针id和mx的使用,以及如何利用回文串特性降低时间复杂度。通过实例解释了算法的执行流程,并提供了Python代码实现。
最低0.47元/天 解锁文章
2136





