找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

167

积分

0

好友

21

主题
发表于 12 小时前 | 查看: 2| 回复: 0

理解并高效解决“最长回文子序列”问题,是提升算法与数据结构能力的重要一环。与要求字符连续的回文子串不同,回文子序列允许字符不连续,这为问题增加了一定的复杂度。本文将详细解析如何运用动态规划(DP)思想来解决这个问题。

核心解题思路:动态规划五部曲

动态规划是解决此类最值问题的经典方法,遵循清晰的五步策略可以有效地将思路转化为代码。

1. 确定DP数组含义

首先定义二维DP数组 dp[i][j],它表示字符串 s 在闭区间 [i, j] 范围内,最长回文子序列的长度

2. 推导状态转移方程

整个算法的关键在于根据字符 s[i]s[j] 是否相等,进行不同的状态转移。

  • 情况一:s[i] == s[j]
    当首尾字符相同时,它们必然包含在 [i, j] 区间的最长回文子序列中。因此,区间 [i, j] 的最长长度等于内部区间 [i+1, j-1] 的最长长度加上首尾这两个字符。

    dp[i][j] = dp[i + 1][j - 1] + 2

    动态规划精解:LeetCode 516.最长回文子序列问题 - 图片 - 1

  • 情况二:s[i] != s[j]
    当首尾字符不同时,它们不可能同时作为回文子序列的首尾。此时,我们需要考虑分别加入 s[i]s[j] 后,哪个能形成更长的回文子序列。即取两种可能中的最大值。

    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

    动态规划精解:LeetCode 516.最长回文子序列问题 - 图片 - 2

将上述逻辑整合,核心递推代码如下:

if (s[i] == s[j]) {
    dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
3. 初始化DP数组

递推公式决定了我们需要对基础情况进行初始化:

  • i == j 时,即区间内只有一个字符,其本身就是一个长度为1的回文序列。因此 dp[i][i] = 1
  • 对于其他情况(i > j 无意义,i < j 但字符不等的情况),可以将 dp[i][j] 初始化为0,以确保后续取最大值操作不会受到初始值干扰。
4. 确定遍历顺序

观察状态转移方程,dp[i][j] 依赖于其 左下方 (dp[i+1][j-1])、正下方 (dp[i+1][j]) 和 正左方 (dp[i][j-1]) 的值。
动态规划精解:LeetCode 516.最长回文子序列问题 - 图片 - 3

因此,为了保证在计算 dp[i][j] 时其所依赖的状态已被计算,我们必须采用特定的遍历顺序:从下到上(i 递减),从左到右(j 递增)

5. 举例推导与验证

以输入 s = "cbbd" 为例,我们可以手动填充DP表来验证算法逻辑。最终 dp[0][3] 的值2即为最长回文子序列(“bb”)的长度。
动态规划精解:LeetCode 516.最长回文子序列问题 - 图片 - 4

完整代码实现 (Java)

以下是基于以上动态规划思路的完整Java实现:

class Solution {
    public int longestPalindromeSubseq(String s) {
        int len = s.length();
        // dp[i][j] 表示 s[i..j] 的最长回文子序列长度
        int[][] dp = new int[len][len];

        // 遍历顺序:从下到上,从左到右
        for (int i = len - 1; i >= 0; i--) {
            // 初始化单个字符的情况
            dp[i][i] = 1;
            // j 从 i+1 开始,因为 j=i 的情况已初始化
            for (int j = i + 1; j < len; j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                } else {
                    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }
        // 返回整个字符串 s[0..len-1] 的结果
        return dp[0][len - 1];
    }
}

总结

通过清晰的五步分析,我们将一个看似复杂的字符串问题,系统地拆解为可编程的动态规划状态转移过程。掌握这种分析方法,对于解决LeetCode上的其他区间DP问题(如“最长回文子串”、“扰乱字符串”等)具有重要的借鉴意义。理解并熟练运用Java等语言实现此类算法,是程序员构建扎实基本功的关键。

动态规划精解:LeetCode 516.最长回文子序列问题 - 图片 - 5




上一篇:基于LLM的图表工具Next AI Draw.io:自然语言对话生成架构图
下一篇:C++未定义行为安全指南:5种常见陷阱与调试策略
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 16:01 , Processed in 0.106135 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表