1、回文子串
class Solution
{
//1、状态表示:
// dp[i][j]:s字符串种从i位置到j位置的子串,是否是回文串
//2、状态转移方程:
//if s[i] != s[j],false
//如果s[i] == s[j]:(1)i == j,true;(2) i+1 == j相邻,true;(3)如果不是前面两种关系,那么就去看dp[i+1][j-1]
//3、初始化:其实不用初始化,因为dp[i+1][j-1]不会越界
//4、填表顺序:dp[i][j]需要dp[i+1][j-1],左下角。所以需要从下往上
//5、找一找dp表中true的个数
public:
int countSubstrings(string s)
{
int len = s.size();
int res = 0;
vector<vector<bool>> dp(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
{
if(s[i] == s[j])
dp[i][j] = i + 1 < j ? dp[i+1][j-1] : true;
if(dp[i][j] == true) res++;
}
return res;
}
};
2、最长回文子串
class Solution
{
//dp[i][j]:存储从[i,j]位置的子串是不是回文串
public:
string longestPalindrome(string s)
{
int len = s.size();
vector<vector<bool>> dp(len,vector<bool>(len));
string res = "";
for(int i = len-1 ;i>=0;i--)
for(int j = i;j<len;j++)
{
if(s[i] == s[j])
dp[i][j] = i + 1 < j ? dp[i+1][j-1] : true;
if(dp[i][j] == true)
{
string newstr(s.begin() + i,s.begin()+j+1);//判断长度
if(res.size() < newstr.size())
{
res = newstr;
}
}
}
return res;
}
};
3、分割回文串Ⅳ
class Solution
{
//dp[i][j]:存储从[i,j]位置的子串是不是回文串
public:
bool checkPartitioning(string s)
{
int len = s.size();
vector<vector<bool>> dp(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
{
if(s[i] == s[j])
dp[i][j] = i + 1 < j ? dp[i+1][j-1] : true;
}
//判断dp[0][j] && dp[j+1][k] && dp[k+1][len-1]
for(int j = 0;j<len-1;j++)
for(int k = j + 1;k <len-1;k++)
{
if(dp[0][j] && dp[j+1][k] && dp[k+1][len-1]) return true;
}
return false;
}
};
4、分割回文串Ⅱ
class Solution
{
//1、状态表示:
// 经验 + 题目要求:
// dp[i]表示:s[0,i]区间上的字符串,最少分割次数
//2、状态转移方程
//(1)[0,i]本身就是回文,-->0
//(2)[0,i]本身不是回文,在[0,i]中取一个j(分割),0 < j <= i,判断从[j,i]是否是回文串
// 如果是,那么[j,i]是0,总的dp[i] = dp[j-1] + 1,这个1代表将字符串切成[0,j-1]和[j,i]
// 则dp[i] = min(dp[i-1] + 1,dp[i]);
// 如果不是,那么就不用考虑,j继续寻找
// 所以用一个judge[x][y]来存储[x,y]这个子串是不是回文
//3、初始化:dp[i] = min(dp[i-1] + 1,dp[i]);由于这里取min,并且j-1不会越界,所以初始化为无穷大
//防止干扰
//4、dp[i]需要dp[j-1],所以从左往右
//5、return dp[n-1];
public:
int minCut(string s)
{
int len = s.size();
vector<vector<bool>> judge(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
{
if(s[i] == s[j])
judge[i][j] = i + 1 < j ? judge[i+1][j-1] : true;
}
vector<int> dp(len,0x3f3f3f3f);
for(int i = 0;i<len;i++)
{
if(judge[0][i] == true) dp[i] = 0;
else
{
for(int j = 1;j<=i;j++)
if(judge[j][i]) dp[i] = min(dp[j-1] + 1,dp[i]);
}
}
return dp[len-1];
}
};
5、最长回文子序列
class Solution
{
//1、dp[i][j]:s字符串里[i,j]区间内的所有的子序列,最长的回文子序列的长度
//2、状态转移方程:
//(1)s[i] == [j]:如果i == j,dp[i][j] = 1;如果i + 1 == j,相邻,,dp[i][j] = 2;
//如果i,j之间有其余字符,那么就是dp[i][j] = dp[i+1][j-1] + 2,+2代表加上i和j
//(2)s[i] != s[j]:那么一定不可能以i开头j结尾,那么区间可以变为[i+1,j]或者[i,j-1]
//所以dp[i][j] = max(dp[i+1][j],dp[i][j-1])
//3、初始化:dp[i][j]需要用到dp[i+1][j]和dp[i][j-1],下面和左边,并且j-1不会越界
//所以不用初始化
//顺序:
//从下往上填写,每一行从左往右
//return dp[0][n-1]
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;//这里包含了dp[i][j] = 2和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][n-1];
}
};
6、让字符串成为回文串的最少插入次数
1312. 让字符串成为回文串的最少插入次数 - 力扣(LeetCode)
class Solution
{
//dp[i][j]:s里面[i,j]区间内的子串,使它成为回文串的最小插入次数
//状态转移方程:
//(1)如果s[i] == s[j],j >= i:
// 如果i == j,则dp[i][j] = 0;
// 如果i + 1 == j相邻,则dp[i][j] = 0;
// 如果i + 1 < j,即i,j中间还有其它字符,则dp[i][j] = dp[i+1][j-1]
//(2)如果s[i] != s[j],
// 可以在[i,j]左边加一个s[j],然后让[i,j-1]成为回文串,dp[i][j] = dp[i][j-1] + 1;
// 也可以在[i,j]右边加一个s[i],然后让[i+1,j]成为回文串,dp[i][j] = dp[i+1][j] + 1;
//因此:dp[i][j] = min(dp[i][j-1] + 1,dp[i+1][j] + 1)
//初始化:
//dp[i][j]需要dp[i+1][j-1],左下角
// 需要dp[i][j-1]和dp[i+1][j],左边和下边,是不会越界的
//所以不用初始化
//顺序:从下往上,从左往右
//return dp[0][n-1]
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];//这里是i+1<=j
else dp[i][j] = min(dp[i][j-1],dp[i+1][j]) + 1;
return dp[0][n-1];
}
};