最长回文子序列–LeetCode516
题目
给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。
示例 1:
输入:"bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb"。
示例 2:
输入:"cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb"。
思路
状态定义
dp[i][j]
表示 s 的第i
个字符到第j
个字符组成的子串中,最长的回文序列长度是多少。注意:这里定义的i
和j
指的是长度而非索引,还要注意区分子序列和子串。
转移方程
- 如果 s 的第
i
个字符和第j
个字符相同的话,则dp[i][j] = dp[i + 1][j - 1] + 2
; - 如果 s 的第
i
个字符和第j
个字符不同的话,说明它俩不可能同时出现在s[i..j]
的最长回文子序列中,那么把它俩分别加入s[i+1..j-1]
中,看看哪个子串产生的回文子序列更长,即dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
;
转移方程中的遍历顺序:状态转移方程中想求 dp[i][j]
需要知道 dp[i+1][j-1]
,dp[i+1][j]
,dp[i][j-1]
这三个位置,所以需要反着遍历。i
从最后一个字符开始往前遍历,j
从 i + 1
开始往后遍历,这样可以保证每个子问题都已经算好了。
初始条件:dp[i][i] = 1
表示单个字符的最长回文序列是 1。
参考思路:
元仲辛的题解:动态规划,四要素
labuladong的题解:子序列问题通用思路|最长回文子序列
class Solution {
public int longestPalindromeSubseq(String s) {
if (s == null) {
return 0;
}
int n = s.length();
int[][] dp = new int[n+1][n+1];
// 初始条件
for (int i = 1; i <= n; i++) {
dp[i][i] = 1;// 单个字符的回文子序列长度为1
}
for (int i = n; i >= 1; i--) {
for (int j = i+1; j <= n; j++) {
if (s.charAt(i-1) == s.charAt(j-1)) {
dp[i][j] = dp[i+1][j-1]+2;
}else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
return dp[1][n];
}
}