任务日期:7.22
题目一链接:647. 回文子串 - 力扣(LeetCode)
思路:1.回文字符串是指正着读和倒过来读一样的字符串。
2.该题的关键在于dp[i][j]的定义:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,数据类型是bool类型
代码:
class Solution {
public:
int countSubstrings(string s) {
int result = 0;
vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false)); //dp[i][j]表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串;初始值都应该是false
//确定遍历顺序和推导公式:dp[i][j]是啥取决于dp[i + 1][j - 1],所以遍历顺序应该是从下到上,从左到右
for(int i = s.size() - 1;i >= 0;i --) {
for(int j = i;j < s.size();j ++) { //根据定义j >= i,因此填充的二维dp数组实际上只有右半部分才可能是true
if(s[i] == s[j]) {
if((j - i) <= 1) { //当i,j之间的长度小于等于1时直接判断dp[i][j]是true
result ++;
dp[i][j] = true;
}
else if(dp[i + 1][j - 1]){ //当i,j之间长度更大时就需要判断dp[i + 1][j - 1]的情况了
result ++;
dp[i][j] = true;
}
}
}
}
return result;
}
};
难点:1.本题dp[i][j]的定义。
2.初始化:由于不能随便一个i,j就能是回文字符串,因此初始化为false
3.确定递推公式和遍历顺序时,先确定递推公式:根据画图可以退出dp[i][j]取决于i,j之间的长度和dp[i + 1][j - 1]的情况;由递推公式得知我们的遍历顺序应该从下到上,从左到右
4.最终返回result即回文子字符串的数量。
题目二链接:516. 最长回文子序列 - 力扣(LeetCode)
思路:与回文字符串的区别:
回文子串是要连续的,回文子序列可不是连续的
思路:
1.本题dp[i][j]的定义与上一题不同:上一题是bool这一题是int类型,表示长度。
代码:
class Solution {
public:
int longestPalindromeSubseq(string s) {
vector<vector<int>> dp(s.size(),vector<int>(s.size,0));//dp[i][j]表示在下标i,j之间的最长回文子序列的长度是dp[i][j]
//初始化:dp[i][j]当i == j时,此时就是一个回文子字符串,即dp[i][j] = 1
for(int i = 0;i < s.size();i ++) {
for(int j = 0;j < s.size();j ++) {
if(i == j) dp[i][j] = 1;
}
}
//确定递推公式和遍历顺序:
for(int i = s.size() - 1;i >= 0;i --) {
for(int j = i + 1;j < s.size();j ++) { //因为对角线的dp二维数组已经初始化,因此不需要再去遍历重新初始化了
if(s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2; //如果而正相等直接长度加2
}
else {
dp[i][j] = max(dp[i + 1][j],dp[i][j - 1]);//如果s[i]与s[j]不相同,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。dp[i + 1][j],dp[i][j - 1]这两个值在之前已经求出来了,所以可以直接代入公式求值
}
}
}
return dp[0][s.size() - 1];//右上角的一定是最大值
}
};
难点:1.递推公式的推导:dp[i][j]可以由少一个元素的两种情况的最大值的得出;或者直接在dp[i + 1][j - 1]基础上加上2。
2.在遍历时,j不能从i开始遍历:因为对角线的dp二维数组已经初始化,因此不需要再去遍历重新初始化了,否则数就错了。