【算法】【动规】回文串系列问题

文章目录

    • 跳转汇总链接
    • [3.1 回文子串](#3.1 回文子串)
    • [3.2 最长回文子串](#3.2 最长回文子串)
    • [3.3 分割回文串 IV](#3.3 分割回文串 IV)
    • [3.4 分割回文串II(hard)](#3.4 分割回文串II(hard))

跳转汇总链接

👉🔗动态规划算法汇总链接


3.1 回文子串

🔗题目链接

给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。

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

  1. 状态表示

    • dpij 表示字符串 s 中以 i 位置开头 j 位置结尾的子串,是否是回文。
  2. 状态转移方程

    • 分析 dp 表,要判断 i, j 位置的子串是否为回文,首先要根据 si 和 sj 的大小判定,具体如下:

      复制代码
      s[i] != s[j], false
      s[i] == s[j], i == j, true
      			  i + 1 == j, true
      			  j - i > 1, s[i+1][j-1] == true, true
      			  			 s[i+1][j-1] == false, false
  3. 初始化

    • 这里主要是i+1j-1 可能会超出需要范围,但是有个隐含条件 i <= j,可以在 for 循环中控制,所以不需要初始化。
  4. 填表顺序

    • 填写 dpij,需要有 i+1j-1,故二维数组从下往上填写。
  5. 返回值

    • dp 中的 true 的出现次数。

🐎代码如下:

cpp 复制代码
class Solution {
public:
    int countSubstrings(string s) {
        int n = s.size();
        vector<vector<bool>> dp(n, vector<bool>(n));
        int ret = 0;

        for(int i = n - 1; i >= 0; i--)
        {
            for(int j = i; j < n; j++)
            {
                // 默认都是 false,只需要处理 true 的位置
                if(s[i] == s[j])
                    dp[i][j] = i + 1 < j ? dp[i+1][j-1] : true;
                if(dp[i][j])
                    ret++;
            }
        }
        return ret;
    }
};

3.2 最长回文子串

🔗题目链接

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

如上题分析,写 dp 方程。

在 dpij 且满足基本约束时,找到 len(即 j - i + 1)的最大值,

同时,由于 dp 表是从下往上(从后往前)填的,正好更新 begin。

🐎代码如下:

cpp 复制代码
class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        int len = 1, begin = 0;
        vector<vector<bool>> dp(n, vector<bool>(n));

        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] && j-i+1 > len)
                    len = j - i + 1, begin = i;
            }
        }
        return s.substr(begin, len);
    }
};

3.3 分割回文串 IV

🔗题目链接

给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,那么返回 true ,否则返回 false 。

当一个字符串正着读和反着读是一模一样的,就称其为 回文字符串 。

还是照上述方法,生成 dp 表,记录是否为回文子串,进行数据预处理;

再将字符分成三部分,依次遍历,如果 相应位置的 dp 值为 true,就可以直接返回啦。

🐎代码如下:

sql 复制代码
class Solution {
public:
    bool checkPartitioning(string s) {
        int n = s.size();

        // 1. 预处理:子串是否是回文
        vector<vector<bool>> dp(n, vector<bool>(n));
        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;

        // 2. 字符串分成三段,枚举就好了
        // [0, i) [i, j) [j, n)
        for(int i = 1; i < n - 1; i++)  // i 是第二段的起始
            for(int j = i + 1; j < n; j++)  // j 是第三段的起始
                if(dp[0][i-1] && dp[i][j-1] && dp[j][n-1])
                    return true;

        return false;
    }
};

3.4 分割回文串II(hard)

🔗题目链接

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。

返回符合要求的 最少分割次数 。

同样先预处理数据,方便判断子串是否是回文串;

剩下的分析方法与 1.4 单词拆分题 一样:

  • dpi 表示 s0, i 位置上的最长字串的最小分割次数;
  • 当分析 dpi 的时候,需要将0, i 分成两部分:
    • 首先是离 i 最近的 j, i,找到能满足是回文的 j,

    • 再找 0, j-1 的最小分割次数,正是和状态表示一样,于是有

      复制代码
      dp[i], [0, i] 是回文,0
      	   [0, i] 不是回文,有 0 < j <= i,[j, i] 是回文,求 min(dp[j]+1)
      	   						 		 [j, i] 不是回文,不考虑

🐎代码如下:

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

        // 1. 预处理:子串是否是回文
        vector<vector<bool>> sub(n, vector<bool>(n));
        for(int i = n - 1; i >= 0; i--)
            for(int j = i; j < n; j++)
                if(s[i] == s[j])
                    sub[i][j] = i+1 < j ? sub[i+1][j-1] : true;
        
        // 2. 分割,是另一个dp问题咯~
        vector<int> dp(n, 0x3f3f3f3f);
        for(int i = 0; i < n; i++)
        {
            if(sub[0][i]) 
                dp[i] = 0;
            else
                for(int j = 1; j <= i; j++)
                    if(sub[j][i])
                        dp[i] = min(dp[j - 1] + 1, dp[i]);
        }
        return dp[n-1];
    }
};

🥰如果本文对你有些帮助,欢迎👉 点赞 收藏 关注,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 若有差错恳请留言指正~~


相关推荐
alphaTao12 分钟前
LeetCode 每日一题 2026/5/25-2026/5/31
算法·leetcode
菜菜的顾清寒15 分钟前
力扣HOT100(41)动态规划-杨辉三角
算法·leetcode·动态规划
Cthy_hy20 分钟前
Python算法竞赛:集合去重+字典映射 核心用法一站式整理
数据结构·python·算法
Deepoch27 分钟前
Deepoc数学大模型:驱动发动机行业数智化转型的底层解
人工智能·算法·deepoc·数学大模型
happymaker062630 分钟前
LeetCodeHot100——盛水最多的容器
数据结构·算法·leetcode·双指针·hot100
3DVisionary30 分钟前
蓝光三维扫描:磁性轴承全尺寸精密3D检测方案
算法·3d·3d检测·蓝光三维扫描·精密检测·磁性轴承·圆度测量
过期动态36 分钟前
【LeetCode 热题 100】三数之和
java·数据结构·算法·leetcode·职场和发展·排序算法
逻辑君40 分钟前
Foresight研究报告【20260010】
人工智能·算法·机器学习
weixin_468466851 小时前
大语言模型原理新手入门指南
人工智能·python·算法·语言模型·自然语言处理·transformer·注意力机制
z200509301 小时前
今日算法(回溯找IP,加检测)
算法·leetcode