目录
LeetCode 647. 回文子串
题目链接:LeetCode 647. 回文子串
思想:还是动归五部曲吧,先考虑dp数组的含义。dp[i][j]标明的是s的子串区间[i,j]如果是回文串的话,其值为true。不然就为false。其实这一点十分聪明,可以省掉单独对回文串的判断,因为一个回文串的确定可以由他去掉两边相同的字母的子串确定。也就是说,如果一个子串[i,j],其中s[i]==s[j],且子窜[i+1,j-1]是回文子串的话,那么[i,j]也是回文子串。第二个就是递推公式了,分为两种情况,第一种情况s[i]!=s[j],那么dp[i][j]=false。第二种情况s[i]==s[j],这时候又可以分为两种情况,当j-1<=1的时候,即子串是由一位或两位组成的时候,让dp[i][j]=true,并让记录结果的数据+1;当j-1>1的时候,这个时候判断区间[i+1][j-1]是否是回文子串,也就是查看dp[i+1][j-1]的值,如果是true,让dp[i][j]=true,并让记录结果的数据+1。初始化将全部数据先初始化为false,如果有需要的话也可以将dp[i][i]初始化为true。然后需要确认遍历顺序,根据递推公式来看,dp[i][j]需要dp[i+1][j-1]来确定,也就是说需要从下往上,从左往右遍历。
代码如下:
int countSubstrings(string s) {
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]) {
if (j - i <= 1) {
result++;
dp[i][j] = true;
}else if (dp[i+1][j-1]) {
result++;
dp[i][j] = true;
}
}
}
}
return result;
}
时间复杂度:O(n^2),空间复杂度:O(n^2)。
LeetCode 516. 最长回文子序列
思想:还是动归五部曲。首先是dp数组的含义,跟上题差不多。本题dp[i][j]是指[i,j]区间内最长的回文子序列长度。其次就是递推公式,还是分为两种情况,第一种是s[i]==s[j],那么dp[i][j]=dp[i+1][j-1]+2;第二种是s[i]!=s[j],此时需要考虑的区间就是[i+1,j]和[i,j-1]查看这两个区间的最长回文子串序列长度,并令dp[i][j]=max(dp[i][j-1],dp[i+1][j]);然后就是初始化,需要将单个字母为一个区间的dp数组初始化为1,因为单个字母就是一个回文串。然后遍历顺序就跟上题一模一样了。
代码如下:
int longestPalindromeSubseq(string s) {
vector<vector<int>> dp(s.size(), vector<int> (s.size(), 0));
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][j-1], dp[i+1][j]);
}
}
}
return dp[0][s.size()-1];
}
时间复杂度:O(n^2),空间复杂度:O(n^2)。
总结
动态规划这一章完了, 但是我感觉自己还是没咋提升。