「代码随想录算法训练营」第四十天 | 动态规划 part13
647. 回文子串
题目链接:https://leetcode.cn/problems/palindromic-substrings/
文章讲解:https://programmercarl.com/0647.回文子串.html
题目难度:中等
视频讲解:https://www.bilibili.com/video/BV17G4y1y7z9/
题目状态:看题解
思路一:动态规划
使用一个二维动规数组dp[i][j]
来保存从s[i]
到s[j]
(或从s[j]
到s[i]
,一样的,本题代码是从j到i)是否属于回文子串。两层遍历该字符串,来记录回文子串的个数。
- 当
s[i] == s[j]
时,要判断一下i
和j
的距离,若i和j的距离为0和1,则表示这是一个回文子串。 - 当
s[i] == s[j]
时,但i
和j
的距离较大,那么就要看其里面的一个是不是回文子串。
代码一:
class Solution {
public:
int countSubstrings(string s) {
int len = s.size();
vector<vector<bool>> dp(len, vector<bool>(len, false));
int ans = 0;
for(int i = len - 1; i >= 0; --i) {
for(int j = i; j < len; ++j) {
if(s[i] == s[j]) {
if(j - i <= 1) {
ans++;
dp[i][j] = true;
} else if(dp[i + 1][j - 1]){
ans++;
dp[i][j] = true;
}
}
}
}
return ans;
}
};
思路二:双指针
使用一个辅助函数extend
来遍历有多少子串,其中i和j分别是两个起点指针,两个指针分别向前和向后遍历,如果遇到i不等于j的时候,就可以返回了,结果就是以i和j为中间点的子串有多少。而在主函数中,两次调用extend
函数分别表示奇子串和偶子串。
代码二:
class Solution {
public:
int countSubstrings(string s) {
int ans = 0;
for(int i = 0; i < s.size(); ++i) {
ans += extend(s, i, i, s.size());
ans += extend(s, i, i + 1, s.size());
}
return ans;
}
int extend(const string &s, int i, int j, int n) {
int ans = 0;
while(i >= 0 && j < n && s[i] == s[j]) {
i--;
j++;
ans++;
}
return ans;
}
};
516. 最长回文子序列
题目链接:https://leetcode.cn/problems/longest-palindromic-subsequence/
文章讲解:https://programmercarl.com/0516.最长回文子序列.html
题目难度:中等
视频讲解:https://www.bilibili.com/video/BV1d8411K7W6/
题目状态:看题解
思路:
维护一个动规数组dp[i][j]
表示从s[i]
到s[j]
的最长回文子序列的大小。
- 当
s[i] == s[j]
时,表示其是一个回文序列,因此将这两个字符加入进来,即dp[i][j] = dp[i + 1][j - 1] + 2
。 - 当
s[i] != s[j]
时,我们就需要找在这个范围里的最大子序列的个数了。
代码:
class Solution {
public:
int longestPalindromeSubseq(string s) {
int len = s.size();
vector<vector<int>> dp(len, vector<int>(len, 0));
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[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][len - 1];
}
};