您的位置:首页 > 编程语言 > Go语言

Manacher's algorithm求最长子回文串算法解析

2018-03-13 13:11 316 查看

Manacher’s algorithm 求最长子回文串

用该算法求解最长回文子串,时间和空间复杂度都是O(n)

这里有篇英文解释,可供参考。算法不太好理解,所以在理解的时候记录下来,怕遗忘。

https://articles.leetcode.com/longest-palindromic-substring-part-ii/

算法思想

1. 准备

首先,对回文子串做处理,每个字符之间加入一个无关字符(“#“),如
abcd
编程
#a#b#c#d#
,这样做好处是,总能把回文变成奇数个,这样只用考虑由中心向两边拓展的回文。

其次,定义需要用到的数组或变量。

S:处理过后的字符串,可以理解成char数组

P:和s对应长度的数组,数组第i位记录着S第i位为中心,除去#之后的最大的回文的长度(也就是说求出来的长度要去除#的数量)。

所以我们要做的就变成了将P数组中的每个值(除去S对应下表为“#”的位置)都求解出来。

对于P数组,我们可以先将S对应下表为“#”的位置全部置为0(因为我们不需要这些数据,也不需要计算它),同时我们总可以得到P[1] = 1。

而接下来的步骤就是根据已经知道的值来计算后面未知的值。这个计算基于这样显而易见的一个规律:一个回文串,在中心位置的左半部分边如果是一个子回文串,那么对称的右半部分也会是一个子回文串。

应用在长度上就是:一个回文串,在中心位置的左半部分边如果是一个子回文串且长度为l,那么对称的右半部分也会是一个子回文串,长度也是l

2. 算法核心

方便理解,再定义一些变量

i:我们目前要求的P[i]的值的下标

center:包含i所在位置的最大的回文子串的中心所在位置的下标

mirror:i关于center的对称点。这个点的P[mirror]已经被求解过

那么按照我们之前所说的规律,P[i] = P[mirror]。

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
T = # b # a # b # c # b # a # b # c # b # a # c # c # b #
P = 0 1 0 3 0 1 0 7 0 ? 0 ...
i = 9
center = 7
mirror = 5


但是发现这个结论有问题,并不是都成立。

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
T = # b # a # b # c # b # a # b # c # b # a # c # c # b #
P = 0 1 0 3 0 1 0 7 0 1 0 ? ...
i = 11
center = 7
mirror = 3


这里展示了一种不成立的情况:按照之前的假设,P[11] = P[3] = 3,但是事实上P[11] = 9,回文串为
abcbabcba
.

而不成立的时候,都是如下情况:

以mirror为中心的最大回文字符串 超过了 以center为中心的最大回文字符串 的控制范围

换句话说,就是以mirror为中心的最大回文字符串的最左端 超过了 以center为中心的最大回文字符串的最左端。

再精确一点,
center-P[center] > mirror - P[mirror]
。注意这里的减的是
P[mirror]
P[center]
而不是
P[mirror]/2
P[center]/2
,因为字符串被填充过
#
,而P记录的长度是不包括
#
的。

基于以上讨论。最终我们只要分为两种情况

center-P[center] < mirror - P[mirror]
,不用计算,P[i] = P[mirror]

若相反,只要按照回文串的判断,一个个比较字符是否相等,得到回文最大的长度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息