算法力扣刷题记录 六十八【131.分割回文串】

前言

回溯章节第六篇。切割问题。

记录 六十八【131.分割回文串】。


一、题目阅读

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串。回文串指字符串从前读和从后读一样。返回 s 所有可能的分割方案

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]

提示:

1 <= s.length <= 16
s 仅由小写英文字母组成

二、尝试实现

思路

  1. 分析题目:输出结果元素是一种完整的分割方案。和字符串章节找回文串有区别
  2. 记录 六十三【回溯章节开篇】中说切割问题也是用回溯解决。并且回溯一定可以构造树形结构,从树形结构,结合前五篇解决组合问题的经验入手。
  3. 以示例1建立树形结构,宽度是字符串的分割点,深度是字符串的长度
    在这里插入图片描述
  4. 对照树形结构写递归函数:
  • 确定返回值:肯定是void类型;
  • 确定参数:
    • int startindex 代表每一层可选分割点的起始位置。类似组合问题中的startindex,每一层从哪些元素中开始选取;比如:startindex=0,指s[0]字符后的分割点;startindex = 1,指s[1]字符后的分割点。
    • string& s 代表输入的字符串。
  • 确定终止条件:当startindex == s.size()代表字符串已经分割结尾时,把temp放入result中,return;
  • 确定单层逻辑:
    • for循环:i从startindex开始,i指定某个下标后的分割点。传给下一层,应该从i+1开始。i < s.size();
    • 确定i之后,先把子串用s.substr函数返回。传入第一个参数是起始位置,第二个参数是长度。
    • 判断这个子串是回文串吗?用reverse反转之后比较是否相等。如果不是,continue,不再深入递归;如果是回文串,放到temp中,递到下一层。
    • 回溯操作:把该层放入temp的子串pop,进入下一轮i。

代码实现

class Solution {
public:
    vector<vector<string>> result;//最终的返回值
    vector<string> temp;//中间结果
    void backtracking(int startindex,string& s){
        //终止条件,一种分割方案完毕
        if(startindex == s.size()){
            result.push_back(temp);
            return;
        }

        //递归
        for(int i = startindex;i < s.size();i++){
            //获得子串
            string sstr = s.substr(startindex,i-startindex+1);//第一个参数是起始位置,第二参数是长度
            string r_sstr = sstr;
            reverse(r_sstr.begin(),r_sstr.end());
            if(sstr != r_sstr){//判断该子串是回文串吗?
                continue;//剪枝
            }

            temp.push_back(sstr);//放入该子串
            backtracking(i+1,s);//递到下一层
            temp.pop_back();//回溯
        }
        return;
    }
    vector<vector<string>> partition(string s) {
        result.clear();
        temp.clear();
        backtracking(0,s);
        return result;
    }
};

三、参考学习

参考学习链接

学习内容

  1. 使用回溯的思路是正确的。回溯使用特点:在一个集合中进行切割。
  2. 解决两个问题:1. 切割方案;2. 判断回文。参考代码实现和二、代码实现对比:
  • 细节一:递归函数参数string& s改成const string& s,为了不对s进行修改,一旦有修改操作会警告。
  • 细节二:终止条件——参考给startIndex >= s.size();个人写startIndex == s.size();这两个都可以。
  • 细节三:参考使用双指针另外写了一个函数调用来判断是否是回文串。而个人在reverse两行完成判断操作。
  1. 优化方法:用动态规划提前知道string s中的任何子串是否是回文串。没学到动态规划,但是先接触一下,从函数中分析动态规划的过程:

    void computePalindrome(const string& s) {
            // isPalindrome[i][j] 代表 s[i:j](双边包括)是否是回文字串 
            isPalindrome.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) {isPalindrome[i][j] = true;}
                    else if (j - i == 1) {isPalindrome[i][j] = (s[i] == s[j]);}
                    else {isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i+1][j-1]);}
                }
            }
        }
    

    这是动态规划的函数。其中isPalindrome是二维矩阵,类型是:vector< vector< bool>>。如下图,一个举例。
    在这里插入图片描述


总结

在这里插入图片描述
(欢迎指正,转载标明出处)

  • 22
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值