📜  Manacher算法-线性时间最长回文子串-第3部分(1)

📅  最后修改于: 2023-12-03 15:32:48.492000             🧑  作者: Mango

Manacher算法-线性时间最长回文子串-第3部分

在第1部分和第2部分中,我们已经介绍了 Manacher 算法的基本思想和实现方法。在本篇文章中,我们将介绍 Manacher 算法的重要应用--线性时间求解最长回文子串,同时还将介绍如何在 Manacher 算法的基础上求解最长回文子序列。

最长回文子串

最长回文子串问题是指在一个字符串中寻找最长的回文子串。回文的定义是正着读和倒着读都一样。例如,在字符串 "abaxabaxabb" 中,最长回文子串为 "abaxaba"。

解法

Manacher 算法可以在线性时间内求解最长回文子串。

我们首先对字符串做一些预处理,将其转换成一个奇数长度的字符串,这可以通过在每个字符之间插入一个特殊字符 "#" 来实现。例如,将字符串 "abaxabaxabb" 转换成 "#a#b#a#x#a#b#a#x#a#b#b#"。这样做的好处是,使得每个回文子串的中心都是一个字符,而不是两个字符或三个字符,从而更方便地进行处理。

接下来,我们用两个指针 i 和 j,分别指向字符串的开头和结尾。然后,我们对每个字符 i,计算它成为中心时的最长回文半径,即 Pal[i]。这一点与第1部分的算法类似。

具体计算方法如下:

  1. 如果 i 在 j 的左边,那么根据回文串的对称性,可以得知 Pal[i] = Pal[2 * id - i]。
  2. 如果 i 在 j 的右边或与 j 重合,那么无法利用 Pal 数组的对称性,只能逐个字符地向两侧扩展,计算 Pal[i] 的值。

在进行这些计算的过程中,我们顺便记录下最长回文子串的起始位置和长度,即回文半径最大的那个位置。时间复杂度为 O(n)。

代码如下:

def manacher(s):
    t = '#' + '#'.join(s) + '#'
    n = len(t)

    P = [0] * n
    C, R = 0, 0
    max_len, center = 0, 0

    for i in range(n):
        if R > i:
            P[i] = min(R-i, P[2*C-i])
        else:
            P[i] = 1

        while i-P[i] >= 0 and i+P[i] < n and t[i-P[i]] == t[i+P[i]]:
            P[i] += 1

        if i + P[i] > R:
            C, R = i, i + P[i]

        if P[i] > max_len:
            max_len, center = P[i], i

    start = (center - max_len) // 2
    end = start + max_len - 1

    return s[start:end+1]
示例

下面是一个示例,展示了如何使用 Manacher 算法求解最长回文子串。

s = "abaxabaxabb"
print(manacher(s))  # "abaxaba"
最长回文子序列

与最长回文子串类似,最长回文子序列问题是指在一个序列中寻找最长的回文子序列。回文的定义是正着读和倒着读都一样。例如,在序列 "BDCABA" 中,最长回文子序列为 "BCBC"。

解法

在第2部分中,我们已经介绍了如何利用 Manacher 算法求解最长回文子序列。具体地,我们可以先将原序列反转,然后求出原序列和反转后的序列的最长公共子序列。由于最长公共子序列必须是一个回文序列,因此最长回文子序列就是最长公共子序列本身。

代码如下:

def lcs(X, Y):
    m, n = len(X), len(Y)
    L = [[0] * (n+1) for _ in range(m+1)]

    for i in range(m+1):
        for j in range(n+1):
            if i == 0 or j == 0:
                L[i][j] = 0
            elif X[i-1] == Y[j-1]:
                L[i][j] = L[i-1][j-1] + 1
            else:
                L[i][j] = max(L[i-1][j], L[i][j-1])

    return L[m][n]

def longest_palindromic_subsequence(s):
    return lcs(s, s[::-1])

时间复杂度为 O(n^2)。

示例

下面是一个示例,展示了如何使用 Manacher 算法求解最长回文子序列。

s = "BDCABA"
print(longest_palindromic_subsequence(s))  # 4
总结

本文介绍了 Manacher 算法的应用--线性时间求解最长回文子串和最长回文子序列。通过对回文的对称性和 Manacher 算法的预处理技巧,我们可以在 O(n) 的时间和 O(n) 的空间内解决这些问题,比传统的动态规划方法更加高效。实际应用中,这些算法可以用于字符串相似度匹配、生物信息学等领域。