方法:动态规划
①确定状态:dp[i][j]表示字符串s的下标范围[i, j]内的最长回文子序列长度;
②转移方程:if (s.charAt(i) == s.charAt(j)) {
//长度为1或2的子序列
if (j - i < 2) {
dp[i][j] = j - i + 1;
//长度大于2的子序列
} else {
dp[i][j] = dp[i + 1][j - 1] + 2;
}
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
或 dp[i][i] = 1;
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]);
}
③初始条件和边界情况:dp[i][i] = 1;当i >j时,dp[i][j] = 0;
④计算顺序:因为dp[i][j] 由 dp[i + 1][j - 1] 推出,所以外层for循环倒序,内层for循环正序。
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
//dp[i][j]表示字符串s的下标范围[i, j]内的最长回文子序列长度
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
if (s.charAt(i) == s.charAt(j)) {
//长度为1或2的子序列
if (j - i < 2) {
dp[i][j] = j - i + 1;
//长度大于2的子序列
} else {
dp[i][j] = dp[i + 1][j - 1] + 2;
}
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
}
return dp[0][n - 1];
}
}
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
//dp[i][j]表示字符串s的下标范围[i, j]内的最长回文子序列长度
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < n; j++) {
if (s.charAt(i) == s.charAt(j)) {
//当j=i+1时,dp[i][j] = dp[i + 1][i] + 2 = 0 + 2 = 2
dp[i][j] = dp[i + 1][j - 1] + 2;
//如果s.charAt(i) != s.charAt(j),
//则s[i]和s[j]不可能同时作为同一个回文子序列的首尾
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
}
return dp[0][n - 1];
}
}