leetCode 131.分割回文串 + 动态规划 + 回溯算法 + 优化 + 图解 + 笔记

news/2024/7/10 4:14:36 标签: 算法, leetcode, 笔记, 回溯算法, 动态规划, 优化, 图解

我的往期文章:

leetCode 647.回文子串 动态规划 + 优化空间 / 中心扩展法 + 双指针-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/133883091?spm=1001.2014.3001.5501leetCode 131.分割回文串 + 回溯算法 + 图解 + 笔记-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/134700907?spm=1001.2014.3001.5501(一)利用动态规划优化判断回文子串

  • 利用动态规划高效地事先一次性计算出, 针对一个字符串s, 它的任何子串是否是回文字串, 然后在我们的回溯函数中直接查询即可, 省去了双指针移动判定这一步骤.(来自代码随想录Carl老师的原话)原文链接:代码随想录 (programmercarl.com)

>>思路和分析

回文子串:讲究的是这个字符串里边左右两边是对称的左右两边的元素是相同的。如果只判断这个字符串的最左面和最右面这两个元素相同的情况下,还知道中间的子串已经是回文的,那么就可以直接判断整个字符串它就是回文子串。

也就是说,如果在[i+1,j-1]范围的子串是一个回文串,再向两边拓展遍历的时候,那只需要判断两边这两个元素是否相同就可以了若相同,dp[i][j]是回文串

>>动规五部曲

1.确定dp数组以及下标的含义

  • dp[i][j]:表示区间范围[i,j]的子串是否为回文子串。如果是,则dp[i][j] = true,否则为false
  • 或者说,dp[i][j] 表示截取从 i 到 j 的子串是否为回文子串

2.确定递推式

if(j == i) dp[i][j]=true;
else if(j-i == 1) dp[i][j] = (s[i]==s[j]);
else dp[i][j] = (s[i] == s[j] && dp[i+1][j-1]);

3.dp 数组初始化

  • dp[i][j]初始化为false

4.确定遍历顺序

一定要从下到上,从左到右遍历,这样能保证dp[i+1][j-1]是经过计算得来的

 5.举例推导dp数组

void computePalindrome(const string& s) {
    // dp[i][j] 代表s[i:j](双边包括)是否是回文子串
    dp.resize(s.size(),vector<bool>(s.size(),false));// 根据字符串s,刷新布尔矩阵的大小
    for(int i=s.size()-1;i>=0;i--) {
        // 需要倒序计算,保证在i行时,i+1行已经计算好了
        for(int j=i;j<s.size();j++) {
            if(j == i) dp[i][j]=true;
            else if(j-i == 1) dp[i][j] = (s[i]==s[j]);
            else dp[i][j] = (s[i] == s[j] && dp[i+1][j-1]);
        }
    }
}
"aebeaeccfcce"
1  0  0  0  1  0  0  0  0  0  0  0  
0  1  0  1  0  0  0  0  0  0  0  0  
0  0  1  0  0  0  0  0  0  0  0  0  
0  0  0  1  0  1  0  0  0  0  0  0  
0  0  0  0  1  0  0  0  0  0  0  0  
0  0  0  0  0  1  0  0  0  0  0  1  
0  0  0  0  0  0  1  1  0  0  1  0  
0  0  0  0  0  0  0  1  0  1  0  0  
0  0  0  0  0  0  0  0  1  0  0  0  
0  0  0  0  0  0  0  0  0  1  1  0  
0  0  0  0  0  0  0  0  0  0  1  0  
0  0  0  0  0  0  0  0  0  0  0  1  


"acgcabbfcc"
1  0  0  0  1  0  0  0  0  0  
0  1  0  1  0  0  0  0  0  0  
0  0  1  0  0  0  0  0  0  0  
0  0  0  1  0  0  0  0  0  0  
0  0  0  0  1  0  0  0  0  0  
0  0  0  0  0  1  1  0  0  0  
0  0  0  0  0  0  1  0  0  0  
0  0  0  0  0  0  0  1  0  0  
0  0  0  0  0  0  0  0  1  1  
0  0  0  0  0  0  0  0  0  1  

(二)分割回文串 + 动态规划 + 回溯算法 + 优化

class Solution {
public:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    vector<vector<bool>> dp; // 放事先计算好的是否回文子串的结果
    void backtracking(const string& s,int startIndex) {
        // 如果起始位置已经大于 s 的大小,说明已经找到了一组分割方案了
        if(startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for(int i=startIndex;i<s.size();i++) {
            if(dp[startIndex][i]) { // 是回文子串
                // 获取[startIndex,i] 在 s 中的子串
                string subStr = s.substr(startIndex,i-startIndex+1);
                path.push_back(subStr);
            }else continue; // 不是回文,跳过
            backtracking(s,i+1);// 寻找 i+1 为起始位置的子串
            path.pop_back();// 回溯过程,弹出本次已经添加的子串
        }
    }
    void computePalindrome(const string& s) {
        // dp[i][j] 代表s[i:j](双边包括)是否是回文子串
        dp.resize(s.size(),vector<bool>(s.size(),false));// 根据字符串s,刷新布尔矩阵的大小
        for(int i=s.size()-1;i>=0;i--) {
            // 需要倒序计算,保证在i行时,i+1行已经计算好了
            for(int j=i;j<s.size();j++) {
                if(j == i) dp[i][j]=true;
                else if(j-i == 1) dp[i][j] = (s[i]==s[j]);
                else dp[i][j] = (s[i] == s[j] && dp[i+1][j-1]);
            }
        }
    }
    vector<vector<string>> partition(string s) {
        computePalindrome(s);
        backtracking(s, 0);
        return result;
    }
};

参考和推荐文章:

代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.html#%E6%80%9D%E8%B7%AF

摘选代码随想录的总结:

  • 总结难点:
  1. 如何切割?切割问题可以抽象为组合问题
  2. 如何模拟那些切割线?
  3. 切割问题中递归如何终止?
  4. 在递归循环中如何截取子串?
  5. 如何判断回文?

递归用于纵向遍历,for循环用于横向遍历当切割线迭代至字符串末尾,说明找到一种方法。类似组合问题,为了不重复切割同一位置,利用 start_index 作为标记,记录下一轮。递归的起始位置(切割线)。切割过的地方不能重复切割,故递归函数传入 i+1


http://www.niftyadmin.cn/n/5229679.html

相关文章

【动态规划】LeetCode-面试题08.01三步问题

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

浅聊代理(应用部署)

以前很少接触过项目的上线部署&#xff0c; 我对前后端交互的认知还停留在前端一个请求 对应后端一个API 比如后端提供: /api/backend/categories -GET 前端则通过使用ajax或者axios组件去构建http请求&#xff0c; 发送到: https://host:port/api/backend/categories -GET 一、…

golang 函数选项模式

一 什么是函数选项模式 函数选项模式允许你使用接受零个或多个函数作为参数的可变构造函数来构建复杂结构。我们将这些函数称为选项&#xff0c;由此得名函数选项模式。 例子&#xff1a; 有业务实体Animal结构体&#xff0c;构造函数NewAnimal&#xff08;&#xff09;&…

【Unity】Blender场景导入

素材 下载场景&#xff1a;https://www.aplaybox.com/details/model/keDSIks72Qh3 blender文件导出为.fbx文件&#xff0c;路径选择复制&#xff08;做的过程太乱了不知道有没有影响&#xff09;&#xff0c;物理类型选择网格&#xff0c;勾选应用变换 blender下的物体长度是u…

91基于matlab的以GUI实现指纹的识别和匹配百分比

基于matlab的以GUI实现指纹的识别和匹配百分比,中间有对指纹的二值化&#xff0c;M连接&#xff0c;特征提取等处理功能。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 91M连接 特征提取 (xiaohongshu.com)

《合成孔径雷达成像算法与实现》_使用CS算法对RADARSAT-1数据进行成像

CSA 简介&#xff1a;Chirp Scaling 算法 (简称 CS 算法&#xff0c;即 CSA) 避免了 RCMC 中的插值操作。该算法基于 Scaling 原理&#xff0c;通过对 chirp 信号进行频率调制&#xff0c;实现了对信号的尺度变换或平移。基于这种原理&#xff0c;可以通过相位相乘代替时域插值…

GoLang语言Map用法

目录 Map 的内部结构 Map 的操作 1. 创建和初始化 2. 添加键值对 3. 获取值 4. 删除键值对 5. 遍历 map 6. 检查键是否存在 注意事项 在Go语言中&#xff0c;map 是一种无序的键值对集合&#xff0c;其中每个键必须是唯一的。以下是关于 map 内部结构和操作的详细解释…

RSA实现中弱密钥漏洞分析(Analyzing Weak Key Vulnerabilities in RSA Implementation)

点我完整下载&#xff1a;《RSA实现中弱密钥漏洞分析》本科毕业论文一万字.doc RSA实现中弱密钥漏洞分析 "Analyzing Weak Key Vulnerabilities in RSA Implementation" 目录 目录 2 摘要 3 关键词 4 第一章 引言 4 1.1 研究背景 4 1.2 研究目的 5 1.3 研究意义 6 第…