一、经验总结
- 对于回文串问题,传统的以i位置为结尾的状态表示已经不能满足要求,无法推导状态转移方程。应该创建一个二维dp表,将所有子串[i, j]的状态表示出来
- 二维dp表的初始化和填表顺序略微复杂,有时需要借助网格图像分析
二、相关编程题
2.1 回文子串
题目链接
647. 回文子串 - 力扣(LeetCode)
题目描述
算法原理
编写代码
class Solution {
public:int countSubstrings(string s) {int n = s.size();vector<vector<bool>> dp(n, vector<bool>(n));int ret = 0;for(int i = n-1; i >= 0; --i) //从下往上{for(int j = i; j < n; ++j) //j>=i{if(s[i] == s[j])dp[i][j] = i+1<j? dp[i+1][j-1]:true;if(dp[i][j]) ++ret;}}return ret;}
};
2.2 最长回文子串
题目链接
5. 最长回文子串 - 力扣(LeetCode)
题目描述
算法原理
编写代码
class Solution {
public:string longestPalindrome(string s) {int n = s.size();vector<vector<bool>> dp(n, vector<bool>(n));int maxlen=0, begin=0;for(int i = n-1; i >= 0; --i){for(int j = i; j < n; ++j){if(s[i] == s[j])dp[i][j] = i+1<j? dp[i+1][j-1]:true;if(dp[i][j] && maxlen<j-i+1){maxlen = j-i+1;begin = i;}}}return s.substr(begin, maxlen);}
};
2.3 分割回文串Ⅳ
题目链接
1745. 分割回文串 IV - 力扣(LeetCode)
题目描述
算法原理
见代码
编写代码
class Solution {
public:bool checkPartitioning(string s) {//1.预处理:用dp表统计所有的子串是否是回文串int n = s.size();vector<vector<bool>> dp(n, vector<bool>(n));for(int i = n-1; i >= 0; --i){for(int j = i; j < n; ++j){if(s[i] == s[j])dp[i][j] = i+1<j? dp[i+1][j-1]:true;}}//2.分割检查:枚举所有的第二个子字符串的起始及结束位置for(int i = 1; i < n-1; ++i){for(int j = i; j < n-1; ++j){if(dp[0][i-1] && dp[i][j] && dp[j+1][n-1])return true;}}return false;}
};
2.4 分割回文串Ⅱ
题目链接
132. 分割回文串 II - 力扣(LeetCode)
题目描述
算法原理
编写代码
class Solution {
public:int minCut(string s) {//1.预处理:用dp1表统计所有的子串是否是回文串int n = s.size();vector<vector<bool>> dp1(n, vector<bool>(n));for(int i = n-1; i >= 0; --i){for(int j = i; j < n; ++j){if(s[i] == s[j])dp1[i][j] = i+1<j? dp1[i+1][j-1]:true;}}//2.动态规划:用dp2表统计[0,i]区间上的最少分割次数vector<int> dp2(n, INT_MAX);for(int i = 0; i < n; ++i){if(dp1[0][i]) dp2[i] = 0;else{//将[0,i]分割成[0,j-1]和[j,i]for(int j = 1; j <= i; ++j)if(dp1[j][i])dp2[i] = min(dp2[i], dp2[j-1]+1);}}return dp2[n-1];}
};
2.5 最长回文子序列
题目链接
516. 最长回文子序列 - 力扣(LeetCode)
题目描述
算法原理
编写代码
class Solution {
public:int longestPalindromeSubseq(string s) {int n = s.size();vector<vector<int>> dp(n, vector<int>(n));for(int i = n-1; i >= 0; --i){dp[i][i] = 1; //处理i==jfor(int j = i+1; j < n; ++j){if(s[i] == s[j])dp[i][j] = dp[i+1][j-1]+2; //统一处理i+1==j和i+1<jelsedp[i][j] = max(dp[i+1][j], dp[i][j-1]);}}return dp[0][n-1];}
};
2.6 变为回文串的最少插入次数
题目链接
1312. 让字符串成为回文串的最少插入次数 - 力扣(LeetCode)
题目描述
算法原理
细节问题:
- j从i+1开始循环,跳过i==j的情况,默认为0
- i+1==j 和 i+1<j 可以统一处理,因为dp表默认为0
- 无需初始化,因为边界情况前面已经特殊处理过了,不会越界
- 根据填表的需要:dp[i][j-1]和dp[i+1][j];填表顺序从下往上,从左往右,
编写代码
class Solution {
public:int minInsertions(string s) {int n = s.size();vector<vector<int>> dp(n, vector<int>(n));for(int i = n-1; i >= 0; --i){for(int j = i+1; j < n; ++j){if(s[i] == s[j])dp[i][j] = dp[i+1][j-1];elsedp[i][j] = min(dp[i+1][j], dp[i][j-1])+1;}}return dp[0][n-1];}
};