【力扣-动态规划】13、回文子串(674)

128 阅读2分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。

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;
    }
};