线性动态规划:最长回文子串(动态规划解法)
字数 1221 2025-11-13 18:44:37

线性动态规划:最长回文子串(动态规划解法)

题目描述
给定一个字符串s,找到s中最长的回文子串。回文串是正着读和反着读都一样的字符串。你需要返回这个最长回文子串。

例如:

  • 输入:"babad",输出:"bab"或"aba"
  • 输入:"cbbd",输出:"bb"

解题过程

1. 问题分析
回文串具有对称性,判断一个子串是否为回文时:

  • 长度为1的子串一定是回文
  • 长度为2的子串需要两个字符相同
  • 长度大于2的子串需要首尾字符相同,且去掉首尾后的子串也是回文

2. 状态定义
我们定义dp[i][j]表示子串s[i...j]是否为回文串(i ≤ j):

  • dp[i][j] = true,表示s[i...j]是回文串
  • dp[i][j] = false,表示s[i...j]不是回文串

3. 状态转移方程
根据回文串的性质,我们可以得到:

  • 当i = j时:dp[i][j] = true(单个字符)
  • 当j = i + 1时:dp[i][j] = (s[i] == s[j])(两个字符)
  • 当j > i + 1时:dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]

4. 初始化
对于所有i = j的情况,dp[i][i] = true
其他情况初始化为false

5. 遍历顺序
由于dp[i][j]依赖于dp[i+1][j-1](即左下方的状态),我们需要:

  • 按子串长度从小到大遍历
  • 先遍历所有可能的子串长度,再遍历起始位置

6. 记录结果
在填充dp表的过程中,我们记录:

  • 当前找到的最长回文子串的起始位置和长度
  • 或者直接记录最长回文子串本身

7. 详细实现步骤
让我们用"babad"作为例子来演示:

步骤1:初始化dp表(5×5矩阵,全部初始化为false)

  b a b a d
b F F F F F
a F F F F F  
b F F F F F
a F F F F F
d F F F F F

步骤2:处理长度为1的子串(都是回文)

对角线全部设为true

步骤3:处理长度为2的子串

  • "ba": b≠a → false
  • "ab": a≠b → false
  • "ba": b≠a → false
  • "ad": a≠d → false

步骤4:处理长度为3的子串

  • "bab": b=b且"a"是回文 → true(新的最长回文)
  • "aba": a=a且"b"是回文 → true(新的最长回文)
  • "bad": b≠d → false

步骤5:处理长度为4的子串

  • "baba": b≠a → false
  • "abad": a≠d → false

步骤6:处理长度为5的子串

  • "babad": b≠d → false

最终找到的最长回文子串是"bab"或"aba"。

8. 算法优化

  • 时间复杂度:O(n²)
  • 空间复杂度:O(n²),可优化为O(n)
  • 实际实现时,我们只需要记录当前最长回文子串的起始位置和长度

9. 代码框架

def longestPalindrome(s: str) -> str:
    n = len(s)
    if n < 2:
        return s
    
    dp = [[False] * n for _ in range(n)]
    start, max_len = 0, 1
    
    # 初始化长度为1的子串
    for i in range(n):
        dp[i][i] = True
    
    # 按子串长度遍历
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            
            if s[i] == s[j]:
                if length == 2:
                    dp[i][j] = True
                else:
                    dp[i][j] = dp[i + 1][j - 1]
            else:
                dp[i][j] = False
            
            if dp[i][j] and length > max_len:
                start = i
                max_len = length
    
    return s[start:start + max_len]

这种方法通过动态规划系统地检查所有可能的子串,确保找到最长的回文子串。

线性动态规划:最长回文子串(动态规划解法) 题目描述 给定一个字符串s,找到s中最长的回文子串。回文串是正着读和反着读都一样的字符串。你需要返回这个最长回文子串。 例如: 输入:"babad",输出:"bab"或"aba" 输入:"cbbd",输出:"bb" 解题过程 1. 问题分析 回文串具有对称性,判断一个子串是否为回文时: 长度为1的子串一定是回文 长度为2的子串需要两个字符相同 长度大于2的子串需要首尾字符相同,且去掉首尾后的子串也是回文 2. 状态定义 我们定义dp[ i][ j]表示子串s[ i...j ]是否为回文串(i ≤ j): dp[ i][ j] = true,表示s[ i...j ]是回文串 dp[ i][ j] = false,表示s[ i...j ]不是回文串 3. 状态转移方程 根据回文串的性质,我们可以得到: 当i = j时:dp[ i][ j ] = true(单个字符) 当j = i + 1时:dp[ i][ j] = (s[ i] == s[ j ])(两个字符) 当j > i + 1时:dp[ i][ j] = (s[ i] == s[ j]) && dp[ i+1][ j-1 ] 4. 初始化 对于所有i = j的情况,dp[ i][ i ] = true 其他情况初始化为false 5. 遍历顺序 由于dp[ i][ j]依赖于dp[ i+1][ j-1 ](即左下方的状态),我们需要: 按子串长度从小到大遍历 先遍历所有可能的子串长度,再遍历起始位置 6. 记录结果 在填充dp表的过程中,我们记录: 当前找到的最长回文子串的起始位置和长度 或者直接记录最长回文子串本身 7. 详细实现步骤 让我们用"babad"作为例子来演示: 步骤1:初始化dp表(5×5矩阵,全部初始化为false) 步骤2:处理长度为1的子串(都是回文) 步骤3:处理长度为2的子串 "ba": b≠a → false "ab": a≠b → false "ba": b≠a → false "ad": a≠d → false 步骤4:处理长度为3的子串 "bab": b=b且"a"是回文 → true(新的最长回文) "aba": a=a且"b"是回文 → true(新的最长回文) "bad": b≠d → false 步骤5:处理长度为4的子串 "baba": b≠a → false "abad": a≠d → false 步骤6:处理长度为5的子串 "babad": b≠d → false 最终找到的最长回文子串是"bab"或"aba"。 8. 算法优化 时间复杂度:O(n²) 空间复杂度:O(n²),可优化为O(n) 实际实现时,我们只需要记录当前最长回文子串的起始位置和长度 9. 代码框架 这种方法通过动态规划系统地检查所有可能的子串,确保找到最长的回文子串。