一、经验总结
- 对于回文串问题,传统的以i位置为结尾的状态表示已经不能满足要求,无法推导状态转移方程。应该创建一个二维dp表,将所有子串[i, j]的状态表示出来
- 二维dp表的初始化和填表顺序略微复杂,有时需要借助网格图像分析
二、相关编程题
2.1 回文子串
题目链接
题目描述
算法原理
编写代码
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 最长回文子串
题目链接
题目描述
算法原理
编写代码
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 分割回文串Ⅳ
题目链接
题目描述
算法原理
见代码
编写代码
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 分割回文串Ⅱ
题目链接
题目描述
算法原理
编写代码
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 最长回文子序列
题目链接
题目描述
算法原理
编写代码
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==j
for(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<j
else
dp[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];
else
dp[i][j] = min(dp[i+1][j], dp[i][j-1])+1;
}
}
return dp[0][n-1];
}
};