刷题记录
- [*647. 回文子串](#*647. 回文子串)
- [*516. 最长回文子序列](#*516. 最长回文子序列)
*647. 回文子串
dp[i][j]存储 i-j 的子串是否是回文串。使用额外的计数器对回文串个数进行记录。
单个字符本身就是回文子串。当子串长度大于1时,两侧的字符相同,则需要判断中间的子串是否是回文串。
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
cpp
// c++
class Solution {
public:
int countSubstrings(string s) {
vector<vector<bool>> dp(s.size()+1, vector<bool>(s.size()+1, 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]){
if(j-i<=1){
dp[i][j] = true;
result++;
}
else if(dp[i+1][j-1]) {
result++;
dp[i][j] = true;
}
}
}
}
return result;
}
};
*516. 最长回文子序列
求的是最长回文子序列而不是子串,因此不需要连续。
dp[i][j]存储的是字符串 s 中 i-j 之间的最长回文子序列长度。
- 当s[i] != s[j]时,dp[i][j] = max(dp[i][j-1], dp[i+1][j]);
- 当s[i] == s[j]时,有两种情况:
- j-i <= 1,即j和i指向同一个字符或相邻的两个字符,则最长子序列长度为1(同一字符)或2(相邻两个字符):
- dp[i][j] = j - i + 1;
- j-i > 1,则需要查看中间子串的最长回文子序列的长度,用中间串的最长回文子序列的长度加上当前两个字符(i和j指向的字符)
- dp[i][j] = dp[i+1][j-i] + 2;
- j-i <= 1,即j和i指向同一个字符或相邻的两个字符,则最长子序列长度为1(同一字符)或2(相邻两个字符):
时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( n 2 ) O(n^2) O(n2)
cpp
// c++
class Solution {
public:
int longestPalindromeSubseq(string s) {
vector<vector<int>> dp(s.size()+1, vector<int>(s.size()+1, 0));
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]) dp[i][j] = max(dp[i][j-1], dp[i+1][j]);
else{
if(j-i<=1) dp[i][j] = j-i+1;
else dp[i][j] = dp[i+1][j-1]+2;
}
if(dp[i][j]>result) result = dp[i][j];
}
/*
// 输出dp
for(int j=0; j<s.size(); j++) cout<<dp[i][j]<<" ";
cout<<endl;
*/
}
return result;
}
};
动态规划系列完结