代码随想录算法训练营第五十二天 | 647.回文子串 516.最长回文子序列
647.回文子串
动态规划法
动规五部曲:
- dp[i][j]: 表示区间范围[i, j]的字串是否是回文串
如果dp[i]表示下表为i的字符串有dp[i]个回文串的话,写不出递推公式,因为dp[i]和dp[i-1]没有什么关系,但如果已经知道i-j位置的字符串已经是回文串的话,只需判断i-1位置和j+1位置是否相等就可以了 - 递推公式:
- s[i] == s[j]:
- 情况一:i == j, 是回文串
- 情况二:j-i == 1, 是回文串
- 情况三: 判断[i+1, j-1]是否是回文串,如果是,则是回文串,否则不是回文串
- s[i] != s[j], 不是回文串
- s[i] == s[j]:
- 初始化:军初始化为false
- 遍历顺序:由dp[i+1][j-1]推出dp[i][j]所以遍历顺序应从下到上,从左往右
- 打印dp数组
class Solution {
public:
int countSubstrings(string s) {
// dp[i][j]: 表示区间范围[i, j]的字串是否是回文串
vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
int count = 0;
// 递推公式:
// s[i] == s[j]:
// 情况一:i == j, 是回文串
// 情况二:j-i == 1, 是回文串
// 情况三: 判断[i+1, j-1]是否是回文串,如果是,则是回文串,否则不是回文串
// s[i] != s[j], 不是回文串
// 初始化:均初始化为false
// 遍历:从下到上,从左往右
for(int i = s.size() - 1; i >= 0; --i) {
for(int j = i; j < s.size(); ++j) { // 求的是范围[i, j] 所以j < i没有意义
if(s[i] == s[j]) {
if(j - i <= 1) {
dp[i][j] = true;
++count;
} else if(dp[i+1][j-1]) {
dp[i][j] = true;
++count;
}
}
}
}
return count;
}
};
516.最长回文子序列
题目链接 文章讲解 视频讲解
动规五部曲:
- dp[i][j]:表示以区间i-j之间最长的回文子序列长度为dp[i][j]
- 确定递推公式:
- s[i] == s[j]: dp[i+1][j-1] + 2
- s[i] != s[j]: i和j不能同时家如,那么分别考虑加入i和加入j。加入i的回文子序列的最长长度为dp[i][j-1],加入j的回文子序列最长长度为dp[i+1][j], 所以dp[i][j] = max(dp[i+1][j], dp[i][j-1])
- 初始化:if(i == j) dp[i][j] = 1;
- 遍历顺序:从下往上,从左往右
- 打印dp数组
class Solution {
public:
int longestPalindromeSubseq(string s) {
// dp[i][j]表示以区间i-j之间最长的回文子序列长度为dp[i][j]
vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
// 递推公式if(s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2;
// s[i] != s[j] dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
// 初始化
for(int i = 0; i < s.size(); ++i) dp[i][i] = 1;
for(int i = s.size() - 1; i >= 0; --i) {
for(int j = i + 1; j < s.size(); ++j) {
if(s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2;
else dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
}
}
return dp[0][s.size() - 1];
}
};