​​647. 回文子串​​

题目描述

给你一个字符串 ​​s​​ ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入: s = "abc"
输出: 3
解释: 三个回文子串: "a", "b", "c"

示例 2:

输入: s = "aaa"
输出: 6
解释: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

动态规划解析

/** 动态规划

1、确定dp数组以及下标的含义
dp[i][j](bool): 表示区间[i,j)的子串是否是回文子串,若是为true,否则为false;
2、确定递推公式
s[i] != s[j]:
dp[i][j] = false;
s[i] == s[j]:
(1) 若 i==j
dp[i][j] = true;
(2) 若 j-i<=1
dp[i][j] = true;
(3) 若 j-i>1, 如 abcba ,需要看 [i+1,j-1)区间(bcb)是否是回文串,
if(dp[i+1][j-1]){
dp[i][j] = true;
}
3、初始化dp数组
当s[i]!=s[j]时,dp[i][j]为false,为了方便计算,这里初始值都设为false;

4、确定遍历顺序
因为dp[i][j]值的确定依赖于 dp[i+1][j-1],所以遍历顺序需要从下到上,从左到右
*/

动态规划代码

class Solution {
public:
int countSubstrings(string s) {
// 定义dp数组并进行初始化
vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
int count = 0; // 记录回文子串的数目
for(int i = s.size()-1;i>=0;i--){
for(int j =i;j<s.size();j++){
// 递推公式
if(s[i] == s[j]){
// 若 i == j 或 j-i=1
if(j-i<=1){
count++;
dp[i][j] = true;
}else if(dp[i+1][j-1]){
count++;
dp[i][j] = true;
}
}
}
}
return count;
}
};

双指针解析

/** 双指针
首先判断是否为回文串,如果是,以该子串为中心,只需要向左右两边扩散即可。
所以子串的中心可能是1个字符,也可能是2个字符(3个字符可以看作是1个字符作为中心,左右各添加一个字符)
计算时,需要分情况:1个字符作为中心点或2个字符作为中心点
*/

双指针代码

class Solution {
public:
int countSubstrings(string s) {
int count = 0;
for(int i = 0 ;i<s.size();i++){
count+= countSub(s,i,i); // 1个字符作为中心点,向两边进行扩散
count+=countSub(s,i,i+1); // 2个字符作为中心点,向两边进行扩散
}
return count;
}
// 记录以s[i]s[j]为中心的子串中的回文串数目
int countSub(const string &s,int i,int j){
int ret = 0;

while(i>=0 && j<s.size()&&s[i] == s[j]){
i--; // 向左扩散
j++; // 向右扩散
ret++; // 回文串数目+1
}
return ret;
}
};