一,回文子串
题目链接:LCR 020. 回文子串 - 力扣(LeetCode)

【问题描述】
求一个字符串中有多少个回文子串,其中一个字符也算是一个回文子串。
【解法】
动态规划
求一个字符串中回文子串的个数,我么可以找到每个回文子串,然后统计个数即可。
首先定义状态表示:
dp[i]:表示子串[i-j],是否是回文串。其中下标i<=j。
状态转移方程的推导:
当s[i]!=s[j]时,也就是子串的第一个字符和最后一个字符不相等,那么肯定不是回文串,所以dp[i][j]=false。
当s[i]==s[j]时,在该情况下,又细分三种情况:
- 如果i==j,那么该子串就是一个字符,是回文串,所以dp[i][j]=true。
- 如果i+1==j,也就是该字符串由两个字符构成,且s[i]==s[j],是回文串,所以dp[i][j]=true。
- 如果i+1<j,表示s[i]和s[j]中还有一些字符,那么dp[i][j]=dp[i+1][j-1]。
初始化dp表时,根据状态表示的定义,我们只会用到dp表主对角线的右上部分,左下部分不会用到,对于状态转移dp[i][j]=dp[i+1][j-1],我们不需要考虑越界的问题,因为前两种情况已经做了判断。
最后,只需统计dp表中dp[i][j]==true的数量即可。
【代码】
class Solution {
public:
int countSubstrings(string s) {
//dp[i]:子串[i-j]是否是回文串
int n=s.size();
int sum=0;//统计结果
vector<vector<bool>> dp(n,vector<bool>(n,false));
for(int i=n-1;i>=0;i--)
for(int j=i;j<n;j++)
{
if(s[i]!=s[j])
{
dp[i][j]=false;
}
else
{
dp[i][j]=i+1<j?dp[i+1][j-1]:true;
}
if(dp[i][j])
sum++;
}
return sum;
}
};
时间复杂度O(N^2),空间复杂度O(N^2)
二,最长回文子串

【题目描述】
求字符串中最长的回文子串。
【解法1】
动态规划
上一题是求回文子串的个数,我们用一个dp表来表示【i-j】是否是回文串。
本题可以直接使用上题 的思路,如果dp[i][j]是回文串,也就是dp[i][j]==true,那么看看该子串的长度是否是最大的,该子串的长度是j-i+1。可以使用一个变量len来记录最长的回文子串的长度,begin来记录该回文串的开始位置i。最后使用substr就可以获取到该子串。
【代码】
class Solution {
public:
string longestPalindrome(string s) {
int n=s.size();
vector<vector<bool>> dp(n,vector<bool>(n));
int len=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])
{
if(len<j-i+1)
{
len=j-i+1;
begin=i;
}
}
}
return s.substr(begin,len);
}
};
时间复杂度:O(N^2),空间复杂度O(N^2)
【解法2】
中心扩展算法
求最长的回文子串。我们可以从一个字符出发,求出它作为中心所能形成的最长回文串的长度。
用一个变量i来记录遍历到字符串的哪一个为位置。
每遍历到一个位置,向两边扩展,用两个变量left和right来记录扩展位置的下标。

回文子串的长度可能是奇数,也可能是偶数,所以需要计算两次:
1,回文子串的长度是奇数,让left=i-1,right=i+1,向两边扩展,如果s[left]==s[right],
left--,right++。最后回文子串的长度就是right-left-1。
2,回文子串的长度是偶数,让left=i-1,right=i(left=i,right=i+1也可以),同理,如果s[left]==s[right],left--,right++。最后回文子串的长度就是right-left-1。
记录这两种情况下回文子串最长的长度,以及该回文子串开始的下标。
【代码】
class Solution {
public:
string longestPalindrome(string s) {
int n=s.size();
int begin=0,len=0;
for(int i=0;i<n;i++)
{
//奇数扩展
int left=i,right=i;
while(left>=0&&right<n&&s[left]==s[right])
{
left--;
right++;
}
if(len<right-left-1)
{
len=right-left-1;
begin=left+1;
}
//偶数扩展
left=i-1,right=i;
while(left>=0&&right<n&&s[left]==s[right])
{
left--;
right++;
}
if(len<right-left-1)
{
len=right-left-1;
begin=left+1;
}
}
return s.substr(begin,len);
}
};
时间复杂度O(N),空间复杂度O(1)
三,回文串分割IV
题目链接:1745. 分割回文串 IV - 力扣(LeetCode)

【题目描述】
对于一个字符串,将他分为任意的三个子串,如果存在这三个子串都是回文串,返回true,否则返回false
【解法】
将一个字符串分割成三个子串,可以想象成在一个字符串中,切两刀,分成三部分,而这两"刀"就是下标,我们用下标i,j将字符串分成三个部分,[0,i-1],[i,j],[j+1,n-1],n为字符串长度。
所以只需判断这三个子串是否是回文串即可。而在第一道题中我们使用一个dp表来表示[i-j]子串是否是回文串。所以,在本题中,我们可以使用dp表来做预处理,然后枚举这三个子串:
0,i-1\],\[i,j\],\[j,n-1\],判断这三个子串是否是回文串。
**【代码】**
class Solution {
public:
bool checkPartitioning(string s) {
//dp表存储回文信息【i,j】
int n=s.size();
vector