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

相关推荐
捕鲸叉13 分钟前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer17 分钟前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
wheeldown1 小时前
【数据结构】选择排序
数据结构·算法·排序算法
观音山保我别报错2 小时前
C语言扫雷小游戏
c语言·开发语言·算法
TangKenny3 小时前
计算网络信号
java·算法·华为
景鹤3 小时前
【算法】递归+深搜:814.二叉树剪枝
算法
iiFrankie3 小时前
SCNU习题 总结与复习
算法
Dola_Pan4 小时前
C++算法和竞赛:哈希算法、动态规划DP算法、贪心算法、博弈算法
c++·算法·哈希算法
小林熬夜学编程4 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
阿洵Rain5 小时前
【C++】哈希
数据结构·c++·算法·list·哈希算法