leetCode 131.分割回文串 + 回溯算法 + 图解 + 笔记

131. 分割回文串 - 力扣(LeetCode)

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

示例 1:

cpp 复制代码
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

cpp 复制代码
输入:s = "a"
输出:[["a"]]

>>思路和分析:

  1. 切割问题,有不同的切割方式
  2. 判断回文

代码随想录的Carl 老师说:"切割问题类似组合问题 "!(这段文字来自代码随想录--分割回文串)

对于字符串abcdef:

  • 组合问题 :选取 一个a 之后,在bcdef 中再去选取第二个 ,选取b 之后在cdef 中再选取第三个.....
  • 切割问题 :切割 一个a 之后,在bcdef 中再去切割第二段 ,切割b 之后在cdef 中再切割第三段.....

>>回溯三部曲:

1).确定回溯函数参数

  • path存放切割后回文的子串
  • result 保存 path,作为结果集
  • startIndex 来控制for循环的起始位置(表示下一轮 递归遍历的起始位置),还用来表示这条切割线.(切割过的地方,不能重复切割,和组合问题也是保持一致的【这句话来自代码随想录】)
cpp 复制代码
vector<vector<string>> result;
vector<string> path; // 放已经回文的子串
void backtracking (const string& s, int startIndex) // startIndex 就用来表示这条切割线

2).递归的终止条件

如果切割线切到了字符串最后面,表示找了一种切割方法,此时终止本层递归!也就是出现这种情况:startIndex >= s.size(),收集结果后直接return;

cpp 复制代码
void backtracking (const string& s, int startIndex) {
    // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
    if (startIndex >= s.size()) {
        result.push_back(path);
        return;
    }
}

3).单层搜索的逻辑

>>问题**(O_O)?思考:在递归循环中如何截取子串**呢?

  • startIndex,i :左闭右闭,表示子串的起始位置和终止位置,有截取区间就可以截取子串。substr(startIndex, i - startIndex + 1);

>>问题**(O_O)?思考:如何判断所截取子串是否为回文子串**呢?

  1. 判断是否为回文子串:isPalindrome(s, startIndex, i) ,用双指针法
  2. 如果是回文,就加入在vector<string> path 中,path用来记录切割过的回文子串
cpp 复制代码
for (int i = startIndex; i < s.size(); i++) {
    if (isPalindrome(s, startIndex, i)) { // 是回文子串
        // 获取[startIndex,i]在s中的子串
        string str = s.substr(startIndex, i - startIndex + 1);
        path.push_back(str);
    } else {                // 如果不是则直接跳过
        continue;
    }
    backtracking(s, i + 1); // 寻找i+1为起始位置的子串
    path.pop_back();        // 回溯过程,弹出本次已经添加的子串
}

注意:切割过的位置 ,不能重复切割 ,应传入下一层 的起始位置为 i + 1,即

  • backtracking(s, i + 1);

(1)判断是否为回文子串

cpp 复制代码
bool isPalindrome(const string& s, int start, int end) {
     for (int i = start, j = end; i < j; i++, j--) {
         if (s[i] != s[j]) {
             return false;
         }
     }
     return true;
 }
cpp 复制代码
bool isPalindrome(string s) {
    int left = 0,right = s.size()-1;
    while(left<=right) {
        if (s[left] != s[right]) return false;
        left++;
        right--;
    }
    return true;
}

(2)C++代码

cpp 复制代码
class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    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 (isPalindrome(s, startIndex, i)) {   // 是回文子串
                // 获取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳过
                continue;
            }
            backtracking(s, i + 1); // 寻找i+1为起始位置的子串
            path.pop_back(); // 回溯过程,弹出本次已经添加的子串
        }
    }
    bool isPalindrome(const string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }
public:
    vector<vector<string>> partition(string s) {
        backtracking(s, 0);
        return result;
    }
};
  • 时间复杂度: O(n * 2^n)
  • 空间复杂度: O(n^2)
cpp 复制代码
class Solution {
public:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    bool isPalindrome(string s) {
        int left = 0,right = s.size()-1;
        while(left<=right) {
            if (s[left] != s[right]) return false;
            left++;
            right--;
        }
        return true;
    }
    void backtracking(string s,int startIndex) {
        // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
        if(startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for(int i=startIndex;i<s.size();i++) {
            // 获取[startIndex,i]在s中的子串
            string subStr = s.substr(startIndex,i-startIndex+1);
            if(isPalindrome(subStr)) path.push_back(subStr);// 是回文子串
            else continue;// 不是回文,跳过
            backtracking(s, i + 1); // 寻找i+1为起始位置的子串
            path.pop_back(); // 回溯过程,弹出本次已经添加的子串
        }
    }
    vector<vector<string>> partition(string s) {
        backtracking(s, 0);
        return result;
    }
};

参考和推荐文章、视频:
带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1c54y1e7k6/?p=67&spm_id_from=pageDriver代码随想录 (programmercarl.com)https://www.programmercarl.com/0131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.html#%E4%BC%98%E5%8C%96

相关推荐
I_belong_to_jesus4 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Code_Solitude11 小时前
C语言动态内存 + 柔性数组 + 数组名退化
c语言·算法·柔性数组
上海潭龙东海科技有限公司11 小时前
客运站管控之动态孪生技术方案(新增一屏管控模块
数据仓库·人工智能·算法·重构·动态规划
文丑颜不良啊12 小时前
LeetCode2821~2840题目解析
java·leetcode
YLCHUP14 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
思麟呀14 小时前
背包DP(01背包+完全背包)
c++·算法
笑容掩饰我的失落15 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
Pniubi16 小时前
力扣24. 两两交换链表中的节点(递归)
算法·leetcode·链表
꧁细听勿语情꧂17 小时前
直接选择排序,堆排序,冒泡排序,递归快速排序、hoare版本、挖坑法、lomuto前后指针,非递归快速排序
c语言·开发语言·数据结构·算法