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

Manacher’s algorithm 求最长子回文串

用该算法求解最长回文子串,时间和空间复杂度都是O(n)
这里有篇英文解释,可供参考。算法不太好理解,所以在理解的时候记录下来,怕遗忘。

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

算法思想

1. 准备

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

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

    • S:处理过后的字符串,可以理解成char数组
    • P:和s对应长度的数组,数组第i位记录着S第i位为中心,除去#之后的最大的回文的长度(也就是说求出来的长度要去除#的数量)。

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

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

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

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

2. 算法核心

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

    • i:我们目前要求的P[i]的值的下标
    • center:包含i所在位置的最大的回文子串的中心所在位置的下标
    • mirror:i关于center的对称点。这个点的P[mirror]已经被求解过
  2. 那么按照我们之前所说的规律,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记录的长度是不包括#的。
  3. 基于以上讨论。最终我们只要分为两种情况

    1. center-P[center] < mirror - P[mirror],不用计算,P[i] = P[mirror]
    2. 若相反,只要按照回文串的判断,一个个比较字符是否相等,得到回文最大的长度。

你可能感兴趣的:(Algorithm)