📜  打印最长回文序列(1)

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

打印最长回文序列

介绍

回文是一种特殊的字符串,它从左往右读和从右往左读是一样的。例如,"racecar"、"level" 和 "madam" 都是回文。

最长回文序列是一个字符串中最长的回文子序列。例如,在字符串 "babad" 中,最长回文序列为 "bab" 或 "aba"。

在本文中,我们将介绍几种方法来打印出一个字符串的最长回文序列。

方法一:暴力搜索
思路

首先,我们可以想到一种朴素的搜索方法:对于所有可能的子序列,判断它们是否为回文序列,并计算它们的长度。然后找出长度最长的回文序列。

具体来说,我们可以按如下步骤进行搜索:

  1. 枚举所有子序列。
  2. 判断子序列是否回文。
  3. 如果是回文,计算它的长度。
  4. 如果比当前最长回文序列要长,就更新最长回文序列。
代码
def longest_palindrome(s: str) -> str:
    n = len(s)
    longest_str = ""
    for i in range(n):
        for j in range(i, n):
            sub_str = s[i:j + 1]
            if sub_str == sub_str[::-1] and len(sub_str) > len(longest_str):
                longest_str = sub_str
    return longest_str
复杂度分析

这种方法的时间复杂度为 $O(n^3)$,因为我们需要枚举所有子序列来进行判断。

空间复杂度为 $O(1)$,因为我们不需要使用额外的空间。

方法二:动态规划
思路

我们可以使用动态规划来解决这个问题。具体来说,我们可以定义一个状态 $dp(i, j)$,表示字符串 $s$ 从 $i$ 到 $j$ 所表示的子串是否是回文。

  • 如果 $s_i$ 等于 $s_j$,那么 $dp(i, j) = dp(i+1, j-1)$。
  • 如果 $s_i$ 不等于 $s_j$,那么 $dp(i, j) = False$。
  • 如果 $j-i \leq 2$,那么 $dp(i, j) = True$。

根据这个定义,我们可以写出如下代码:

代码
def longest_palindrome(s: str) -> str:
    n = len(s)
    dp = [[False] * n for _ in range(n)]
    longest_str = ""
    for i in range(n - 1, -1, -1):
        for j in range(i, n):
            if (s[i] == s[j] and (j - i <= 2 or dp[i + 1][j - 1])):
                dp[i][j] = True
                if j - i + 1 > len(longest_str):
                    longest_str = s[i:j + 1]
    return longest_str
复杂度分析

这种方法的时间复杂度为 $O(n^2)$,因为我们只需要枚举所有长度大于等于 $3$ 的子串,而对于每个子串,我们只需要 $O(1)$ 的时间来计算它是否回文。

空间复杂度为 $O(n^2)$,因为我们需要用一个二维数组来存储状态。

方法三:中心扩散
思路

我们可以使用中心扩散来解决这个问题。具体来说,我们可以枚举每个可能成为回文中心的位置,然后尝试向两边扩展,直到无法扩展为止。

具体来说,我们可以按如下步骤进行搜索:

  1. 枚举所有可能成为回文中心的位置。
  2. 尝试向两边扩展,直到无法扩展为止。
  3. 如果比当前最长回文序列要长,就更新最长回文序列。
代码
def longest_palindrome(s: str) -> str:
    n = len(s)
    longest_str = ""
    for i in range(n):
        # 中心为一个字符
        left, right = i, i
        while left >= 0 and right < n and s[left] == s[right]:
            left -= 1
            right += 1
        if right - left - 1 > len(longest_str):
            longest_str = s[left + 1:right]
        # 中心为两个字符
        left, right = i, i + 1
        while left >= 0 and right < n and s[left] == s[right]:
            left -= 1
            right += 1
        if right - left - 1 > len(longest_str):
            longest_str = s[left + 1:right]
    return longest_str
复杂度分析

这种方法的时间复杂度为 $O(n^2)$,因为我们需要枚举所有可能成为回文中心的位置,并尝试向两边扩展。

空间复杂度为 $O(1)$,因为我们不需要使用额外的空间。

总结

本文介绍了三种方法来打印一个字符串的最长回文序列:暴力搜索、动态规划、中心扩散。其中,动态规划是最优的方法,因为它的时间复杂度和空间复杂度都比其他两种方法要低。