返回

揭秘 LeetCode 5:征服最长回文子串,一步步剖析必胜之道

见解分享

最长回文子串:动态规划详解

回文子串:什么是回文?

回文子串是一个字符串,无论从左到右还是从右到左读,都能得到相同的字符串。例如,“aba”和“aa”都是回文子串。回文子串是字符串算法中的一个经典概念,也是 LeetCode 第 5 题:“最长回文子串”的关键。

动态规划:逐层攻克复杂问题

动态规划是一种将大问题分解成小问题,逐步解决并最终解决大问题的强大算法技术。在解决 LeetCode 5 时,我们将使用动态规划来找出字符串中长度最大的回文子串。

LeetCode 5:解题步骤

1. 定义子问题和状态

  • 子问题: 字符串 s 中长度为 n 的区间 [i, j] 的子串是否为回文子串?
  • 状态: dp[i][j] = true,表示子串 [i, j] 是回文子串,否则为 false。

2. 初始化边界条件

  • 对于长度为 1 的子串,dp[i][i] = true。
  • 对于长度为 2 的子串,dp[i][i+1] = true 当且仅当 s[i] = s[i+1]。

3. 状态转移方程

  • 当子串长度大于 2(j - i > 2)时,dp[i][j] 的计算需要考虑 s[i] 和 s[j] 的关系:
    • 如果 s[i] = s[j],则 dp[i][j] = dp[i+1][j-1]。
    • 如果 s[i] ≠ s[j],则 dp[i][j] = false。

4. 计算顺序

  • 从长度为 1 的子串开始计算,逐步扩展到长度为 n 的子串。
  • 对于每个长度为 n 的子串,从左到右逐个计算区间 [i, j] 的子串是否为回文子串。

示例代码

def longest_palindrome(s):
    n = len(s)
    dp = [[False] * n for _ in range(n)]

    # 初始化边界条件
    for i in range(n):
        dp[i][i] = True
    for i in range(n - 1):
        if s[i] == s[i + 1]:
            dp[i][i + 1] = True

    # 状态转移
    for length in range(3, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            if s[i] == s[j]:
                dp[i][j] = dp[i + 1][j - 1]

    # 找出最长回文子串的长度和起始位置
    max_length = 0
    start = 0
    for i in range(n):
        for j in range(n):
            if dp[i][j] and j - i + 1 > max_length:
                max_length = j - i + 1
                start = i

    return s[start:start + max_length]

总结:掌握动态规划的利器

动态规划是解决各种算法问题的一把利器,尤其是字符串算法。通过将复杂问题分解成一系列较小的子问题,我们能够逐步解决并最终找到最优解。LeetCode 5 最长回文子串就是一个很好的例子,展示了动态规划如何帮助我们解决看似复杂的问题。

常见问题解答

  • Q1:动态规划与穷举搜索有什么区别?
    A1:动态规划通过保存中间结果来避免重复计算,而穷举搜索则会反复尝试所有可能的解,效率较低。
  • Q2:动态规划适用于哪些类型的算法问题?
    A2:动态规划适用于具有重叠子问题和最优子结构的算法问题,例如最长公共子序列、背包问题和编辑距离。
  • Q3:为什么状态转移方程在动态规划中至关重要?
    A3:状态转移方程定义了如何从较小的子问题转移到较大的子问题,是动态规划算法的核心。
  • Q4:如何确定动态规划算法的复杂度?
    A4:动态规划算法的复杂度取决于子问题的数量和求解每个子问题的复杂度。
  • Q5:动态规划算法的空间复杂度是多少?
    A5:动态规划算法的空间复杂度通常取决于子问题的数量和存储中间结果所需的空间。