Manacher算法
Manacher算法
该算法用于解决“最长回文子串”的问题
例题:5. 最长回文子串
暴力来解题的话,一般时间复杂度是$O(n^2)$,使用中心扩展法或者二维动态规划
而Manacher算法能$以O(n)$的时间复杂度来解决这个问题。
以下撰写内容参考自灵神b站视频讲解,感谢灵神!
为什么Manacher算法有这种魔力呢?关键在于它能**$O(1)$地判断任意子串是否回文**
比如:
说明:每个字母下方数字表示以该字母为中心,所能得到的最长回文子串的长度,假设目前已经拿到了这些值,那么如何$O(1)$地向下判断剩余的字母(以他们为中心)呢?
比如如下红色框选了c b a b c ,那么这个子串的中心就是a,而a的长度是11(蓝色框),显然这个红色框是在蓝色框之内的,要是依次把红色框之外、蓝色框之内的d、a、b去掉,得到的c b a b c显然是回文的。
又如下,要求右边c的最长回文子串长度,怎么利用之前已经求过的信息呢?
重点:想象有一面镜子,以a为中心,那么下标为8位置的c的镜像位置的c’就是下标为5的这个。下标为5的这个c’的最长回文子串长度我们是知道的,是5,而这个长度对于下标位置为8的c来说没有超出蓝色盒子的右边界,所以以下标8位置为中心的最长回文子串长度就是5,省去了暴力做法(以它为中心,然后左右指针不断扩展……现在不需要了)。这就是算法的核心思想。
对于下标为8的c,要比较7和9是否相同,等价于比较3和5是否相同;要比较6和10是否相同,等价于比较2和6是否相同。但是7和9,6和10都不需要比较,因为8的镜像位置是4,而4已经告诉我们了。
但是如果出界(出了蓝色盒子右边界boxR)
这里贴出对应这一思想的代码:
1 | for(int i=2;i<halfLen.length;i++){ |
为了实现算法,还有其他问题要解决,那就是偶数串的回文中心怎么办?总不能夹在两个字母中间。也有比较好的方法:
那就是原先的字符串s每两个中间插入一个’#‘转变为新字符串t,但是前后都要加’#'且最前和最后还要加入两个不同的字符(用于防止下标出界)

最左边加’^‘最右边加’$‘是因为这两个字符与其他字符均不同,所以在进行扩展时while(s[i]==s[j])判断时,肯定不会使得i或者j出界。起到了哨兵的作用。而中间’#‘的插入,使得当’#'作为子串中心点时,相当于原字符串是以偶字符串在进行扩展;当以字母为中心点时,相当于原字符串是以奇字符串在进行扩展。
定义halfLen[i]:例如c下面的5,代表的是扩展后的字符串t,以c为中心点时,最长回文子串的长度的一半(包括c自身)
那么原字符串s和新字符串t的对应关系呢?试着举个简单例子推导一下:

所以有公式$i_t = 2*i_s + 2$。故$i_s = \frac{i_t-2}{2}$

解释:当我们要判断$s[L,R]$区间是否为回文串时,对应的就是判断$t[2L+2,2R+2]$区间是否是回文串,那么t的这个区间的中心点就是$\frac{2L+2R+4}{2}=L+r+2$,因此只需要看$halfLen[L+R+2]$的值就可以了。
剩下的需要仔细看代码怎么写的,懂了大体思路看代码会容易些:
下面直接使用灵神代码
作者:灵茶山艾府
链接:https://leetcode.cn/problems/longest-palindromic-substring/solutions/2958179/mo-ban-on-manacher-suan-fa-pythonjavacgo-t6cx/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1 | class Solution { |




