647. 回文子串
关键点1:dp数组的含义
dp[i][j]:s字符串下标i到下标j的子串是否是一个回文串,即s[i, j]
关键点2:递归公式的推导
if(s.charAt(i) == s.charAt(j))有两种情况
情况1:j-i<=1; j=i代表一个字母,是回文子串,j-i=1代表i,j相邻且相等,是回文子串,所以返回true
情况2:j-i >1;i和j同时向里移动,所以为i+1,j-1;dp[i][j] = dp[i+1][j-1];
if(s.charAt(i) != s.charAt(j)),如果不相等:则返回false,因为初始化dp数组为false,所以这条为默认情况,可以不写
关键点3:dp数组初始化
dp数组全部初始化为false
关键点4:遍历顺序
由于dp[i][j] = dp[i+1][j-1];
所以下一个dp值与它左下方的dp值有关,因此for循环从下往上遍历(i)从左到右遍历;j从i开始,因为j>=i
class Solution {
public int countSubstrings(String s) {
int len = s.length();
int res = 0;
//dp[i][j]:s字符串下标i到下标j的子串是否是一个回文串,即s[i, j]
boolean[][] dp = new boolean[len][len];
// 初始化,都为false
for(int i = len-1;i >= 0;i--){
for(int j = i;j < len;j++){
if(s.charAt(i) == s.charAt(j)){
if(j-i <= 1){
dp[i][j] = true;
}else{
dp[i][j] = dp[i+1][j-1];
}
}
if(dp[i][j]){
res++;
}
}
}
return res;
}
}
516.最长回文子序列
关键点1:dp数组的含义
dp[i][j]:从下标i到下标j的子串的最长回文子序列的长度
关键点2:递归公式的推导
if(s.charAt(i) == s.charAt(j)),dp[i][j] = dp[i+1][j-1]+2;
if(s.charAt(i) != s.charAt(j)),如果不相等,则有两种情况:
情况1:dp[i][j-1],j往里走一个
情况2:dp[i+1][j],i往里走一个
情况1与情况2求一个最大值
关键点3:dp数组初始化
i,j往里走到最中间的位置,为同一个元素,则回文子序列的长度为1;所以dp[i][i] = 1;
关键点4:遍历顺序
由于dp[i][j] = dp[i+1][j-1];
所以下一个dp值与它左下方的dp值有关,因此for循环从下往上遍历(i)从左到右遍历;j从i+1开始,因为j>=i,j=i的情况已经初始化了,所以j >i
class Solution {
public int longestPalindromeSubseq(String s) {
//dp[i][j]:从下标i到下标j的子串的最长回文子序列的长度
int len = s.length();
int[][] dp = new int[len][len];
// 初始化
for(int i = 0;i < len;i++){
dp[i][i] = 1;
}
for(int i = len-1;i >= 0; 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][j-1],dp[i+1][j]);
}
}
}
return dp[0][len-1];
}
}