目录
题目链接:647. 回文子串
思路
判断一个子字符串(字符串的下标范围[i,j])是否回文,依赖于,子字符串(下表范围[i + 1, j - 1])) 是否是回文。
①dp数组,dp[i][j]表示区间范围[i,j]是否为回文串,是的话为true,不是的话为false
②递推公式,如果s[i] != s[j]则为false,如果相等, 又有以下三种情况:下标相等;下标差一;dp[i+1][j-1]为true
③dp数组初始化,全部初始化为false
④遍历顺序,根据递推公式可知,一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的
⑤推导dp数组
代码
class Solution {
public:
int countSubstrings(string s) {
// dp数组定义以及初始化,全部初始化为false
vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
int result = 0; // 记录回文子串数目
// 遍历顺序,从下往上,从左往右
for (int i = s.size() - 1; i >= 0; i--) {
for (int j = i; j < s.size(); j++) {
// 相等时
if (s[i] == s[j]) {
// 下标相等或者差1
if (j - i <= 1) {
result++;
dp[i][j] = true;
}
// 由递推公式,先判断之前的区间是否是回文子串
else if (dp[i + 1][j - 1]) {
result++;
dp[i][j] = true;
}
}
}
}
return result;
}
};
题目链接:516.最长回文子序列
思路
①dp数组,dp[i][j]表示在区间[i,j]中最长回文子序列的长度
②递推公式,相等时同时加入两个字符,不相等时加入使长度最长的那个字符
s[i] == s[j]时,dp[i][j] = dp[i+1][j-1] + 2;
不相等时,dp[i][j] = max(dp[i+1][j], dp[i][j-1])
③dp数组初始化,i与j相等时为1,因为单独一个字符也算回文,其余为0
④遍历顺序,从下到上,从左到右
⑤推导dp数组
代码
class Solution {
public:
int longestPalindromeSubseq(string s) {
// 定义dp数组
vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
// dp数组初始化
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];
}
};
总结
①回文子串,一个字符串无论从左到右还是从右到左读都一样
②判断是从中间向两边延申,一个字符和两个字符肯定是回文子串,如果左右各加一个字符,就判断这两个字符是否相等,如果相等,则这个长字符串为回文子串
③这是目前遇到的唯一dp数组定义与题目所求不一样的题目
④明确子字符串和子序列的区别,前者必须连续,后者不需要
⑤要根据递推公式来确定初始化以及遍历顺序
⑥最后的结果要么是判断的最值,要么是dp数组推导的终点