《LeetCode647 回文子串 || LeetCode 5 最长回文子串》

一、题目

二、做题思路

2.1 状态表示(核心基础)

本题要求统计字符串中回文子串的总数 。定义 dp[i][j] 表示子串 s[i..j](包含两端)是否为回文串true 表示是,false 表示否)。该二维表可覆盖所有连续子串。

2.2 状态转移方程(关键难点)

判断 s[i..j] 是否为回文串,需满足两个条件:

  • 首尾字符相等:s[i] == s[j]

  • 内部子串 s[i+1..j-1] 为回文串,或子串长度小于等于 2(此时无需检查内部)。

    因此转移为:dp[i][j] = (s[i] == s[j]) && (i+1 > j-1 ? true : dp[i+1][j-1]) ,即当 i+1 >= j-1(长度 ≤ 2)时直接为 true,否则依赖 dp[i+1][j-1]

2.3 初始化(边界防护)

所有 dp[i][j] 初始化为 false。长度 1 的子串(i == j)必然回文,会在递推中被正确置 true(因为首尾相同且内部为空)。

2.4 填表顺序(递推方向)

dp[i][j] 依赖 dp[i+1][j-1](左下位置),因此 从下到上in-10),且内层 从左到右jin-1),确保 dp[i+1][j-1] 已计算。

2.5 返回值(目标映射)

遍历所有 dp[i][j],每当为 true 时计数器 ret++,最终 返回 ret,即为回文子串总数。

三、代码

cpp 复制代码
class Solution 
{
public:
    int countSubstrings(string s) 
    {
        int n = s.size();

        // 1. 创建dp表
        // dp[i][j] 表示子串 s[i..j] 是否是回文串(i <= j)
        // 初始化为 false
        vector<vector<bool>> dp(n, vector<bool>(n, false));

        int ret = 0; // 记录回文子串的总数

        // 2. 填表顺序:从下往上(i 从 n-1 到 0),从左往右(j 从 i 到 n-1)
        //    因为 dp[i][j] 依赖于 dp[i+1][j-1](更短的内部子串),
        //    所以需要先计算行号更大的 dp,即 i 递减方向遍历。
        for (int i = n - 1; i >= 0; i--) 
        {
            for (int j = i; j < n; j++) 
            {
                // 3. 状态转移方程:
                // 若 s[i] == s[j],则 s[i..j] 是否为回文取决于内部子串 s[i+1..j-1] 是否为回文,
                // 但当 i+1 > j-1(即长度 ≤ 2)时,直接判定为 true。
                if (s[i] == s[j]) 
                {
                    dp[i][j] = (i + 1 < j) ? dp[i + 1][j - 1] : true;
                }
                // 若 s[i] != s[j],dp[i][j] 保持 false(已默认)

                // 4. 若当前子串是回文,累加计数
                if (dp[i][j] == true) 
                {
                    ret++;
                }
            }
        }

        // 5. 返回值:回文子串的总个数
        return ret;
    }
};

四、流程图

五、题目

六、做题思路

6.1 状态表示(核心基础)

本题要求返回最长的回文子串 。定义 dp[i][j] 表示子串 s[i..j](包含两端)是否为回文串true 表示是,false 表示否)。通过二维表覆盖所有连续子串,便于记录最长结果。

6.2 状态转移方程(关键难点)

判断 s[i..j] 是否为回文,需满足:

  • 首尾字符相等:s[i] == s[j]

  • 若子串长度 ≤ 2(即 i+1 >= j),则直接为 true;否则需内部子串 s[i+1..j-1] 也为回文,即 dp[i+1][j-1] == true

    因此转移为:dp[i][j] = (s[i] == s[j]) && (i+1 >= j ? true : dp[i+1][j-1])

6.3 初始化(边界防护)

所有 dp[i][j] 初始化为 false。长度 1 的子串(i == j)会在递推中因首尾相同且长度≤2被置为 true

6.4 填表顺序(递推方向)

dp[i][j] 依赖 dp[i+1][j-1](左下位置),因此 从下到上in-10),内层 从左到右jin-1),确保内部子串已先计算。

6.5 返回值(目标映射)

在遍历过程中,每当 dp[i][j] == true 且当前长度 j-i+1 大于已记录的最长长度 len 时,更新 len 和起始下标 begin。最终 返回 s.substr(begin, len),即最长回文子串。

七、代码

cpp 复制代码
class Solution 
{
public:
    string longestPalindrome(string s) 
    {
        int n = s.size();

        // 1. 创建dp表
        // dp[i][j] 表示子串 s[i..j] 是否为回文串(i <= j)
        // 初始化为 false,表示默认不是回文
        vector<vector<bool>> dp(n, vector<bool>(n, false));

        int len = 1;    // 当前最长回文子串的长度
        int begin = 0;  // 当前最长回文子串的起始下标

        // 2. 填表顺序:从下往上(i 从 n-1 到 0),从左往右(j 从 i 到 n-1)
        //    因为 dp[i][j] 依赖于 dp[i+1][j-1](更短的内部子串),
        //    所以需要先计算行号更大的 dp,即 i 递减。
        for (int i = n - 1; i >= 0; i--) 
        {
            for (int j = i; j < n; j++) 
            {
                // 3. 状态转移方程:
                // 只有当 s[i] == s[j] 时,才可能成为回文。
                // 若子串长度 ≤ 2(即 i+1 >= j),则直接判定为 true;
                // 否则取决于内部子串 s[i+1..j-1] 是否为回文。
                if (s[i] == s[j]) 
                {
                    dp[i][j] = (i + 1 < j) ? dp[i + 1][j - 1] : true;
                }
                // 若 s[i] != s[j],dp[i][j] 保持 false

                // 4. 若当前子串是回文且长度大于已记录的最长长度,则更新记录
                if (dp[i][j] && (j - i + 1) > len) 
                {
                    len = j - i + 1;
                    begin = i;
                }
            }
        }

        // 5. 返回值:截取并返回最长回文子串
        return s.substr(begin, len);
    }
};

八、流程图

相关推荐
一次旅行44 分钟前
RLHF全链路深度解析:Reward Model数学推导+PPO完整实战,对比GRPO轻量化方案
人工智能·算法·机器学习
Wang's Blog1 小时前
AI Agent白手起家29: Few Shot 提示词工程实战
人工智能·算法
June`1 小时前
warp shuffle指令
c++·人工智能·算法·cuda
geovindu1 小时前
go:Bit Operation Algorithm
开发语言·后端·算法·golang·位运算法
乱七八糟的屋子2 小时前
【C++数值计算】NumCpp超详细入门教程(纯正Numpy语法、零学习成本、C++首选科学计算库、性能压测对比)
c++·数值计算·numpy·高性能计算·科学计算·ndarray
choumin2 小时前
C程序能调用extern “C“声明的带默认参数的C++函数吗?
c语言·c++·extern c·默认参数
AmyLin_20012 小时前
PDF 色彩保真工程实践【3】上手实践:工程结构、依赖集成与一键构建运行
c++·visualstudio·pdf·zlib·vcpkg·libtiff·cmyk
并不喜欢吃鱼2 小时前
从零开始 C++------ 十六.深度拆解 C++ 智能指针:unique_ptr/shared_ptr/weak_ptr 底层模拟、内存泄漏根治
开发语言·c++
Wang's Blog2 小时前
AI Agent白手起家31: 根据语义相似度动态选择 Few Shot 示例
人工智能·算法
不相心 -w-3 小时前
Cmake的基础用法
linux·开发语言·c++