• 代码随想录算法训练营第23期day26|39. 组合总和、40.组合总和II、131.分割回文串


    目录

    一、(leetcode 39)组合总和

    二、(leetcode 40)组合总和II

    三、(leetcode 131)分割回文串


    一、(leetcode 39)组合总和

    力扣题目链接

    状态:基本回溯AC,剪枝优化未AC。

    基本的回溯方法遵循回溯模板,需要注意的是回溯中的startIndex是从当前i继续而不是i+1,因为组合中的数字允许重复。剪枝优化的思路是通过target这个限制条件。在基本回溯的过程中,终止条件是当前层的sum大于target,但如果之后的数比当前数大,那么后续的递归是多余的。因此可以同通过先排序,再防止进入大于target下一层的操作来进行剪枝优化。

    1. class Solution {
    2. public:
    3.     vectorint>> res;
    4.     vector<int> path;
    5.     void backtracking(vector<int>& candidates, int target, int sum, int startIndex){
    6.         if(sum == target){
    7.             res.emplace_back(path);
    8.             return;
    9.         }
    10.  
    11.         int len =candidates.size();
    12.         for(int i = startIndex; i < len && sum + candidates[i] <= target; ++i){
    13.             sum += candidates[i];
    14.             path.emplace_back(candidates[i]);
    15.             backtracking(candidates, target, sum, i);
    16.             sum -= candidates[i];
    17.             path.pop_back();
    18.         }
    19.     }
    20.     vectorint>> combinationSum(vector<int>& candidates, int target) {
    21.         res.clear();
    22.         path.clear();
    23.         sort(candidates.begin(), candidates.end());
    24.         backtracking(candidates, target, 0, 0);
    25.         return res;
    26.     }
    27. };

    二、(leetcode 40)组合总和II

    力扣题目链接

    状态:查看思路后AC。

    这题乍一看和上一题一样,无非就是同一个元素不能重复使用,似乎只要将层中的递归startIndex修改为i+1即可。但是这道题还有另一个难点,那就是数组中会出现重复的元素,同一位置的元素不能重复使用,同时最终答案也不能出现重复path,这就是一个判断树层去重(而不是树枝去重)的过程,为此加入一个used数组作为辅助判断。时间复杂度,空间复杂度。

    1. class Solution {
    2. public:
    3.     vectorint>> res;
    4.     vector<int> path;
    5.     void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used){
    6.         if(sum == target){
    7.             res.emplace_back(path);
    8.             return;
    9.         }
    10.         int len = candidates.size();
    11.         for(int i = startIndex; i < len && sum + candidates[i] <= target; ++i){
    12.             if(i > 0 && candidates[i] == candidates[i-1] && used[i-1] == false){
    13.                 continue;
    14.             }
    15.             sum += candidates[i];
    16.             path.emplace_back(candidates[i]);
    17.             used[i] = true;
    18.             backtracking(candidates, target, sum, i+1, used);
    19.             sum -= candidates[i];
    20.             path.pop_back();
    21.             used[i] = false;
    22.         }
    23.     }
    24.     vectorint>> combinationSum2(vector<int>& candidates, int target) {
    25.         res.clear();
    26.         path.clear();
    27.         sort(candidates.begin(), candidates.end());
    28.         vector<bool> used(candidates.size(), false);
    29.         backtracking(candidates, target, 0, 0, used);
    30.         return res;
    31.     }
    32. };

    这道题也可以不用used数组,直接判断与前一个位置相同的元素的下标是否是本身,即

    if(i > startIndex && candidates[i] == candidates[i-1]) continue;

    三、(leetcode 131)分割回文串

    力扣题目链接 

    状态:有思路但是没有代码实现,动态规划判断回文没想到。

    这道题的主要步骤可以拆分成两个部分,如何分割和如何判断。如何分割其实就是组合问题,我们需要对每个组合进行判断,很自然地想到了回溯方法来进行穷举;至于如何判断,想到的第一种方法就是双指针一左一右进行判断(还有利用栈的特性,不过不比双指针效率高)。可以优化的部分就是利用动态规划对回文串进行判断,一个回文串的去两端子串也一定是回文串。但是实际的代码编写,还需要练习。主要难点如下:

    • 切割问题可以抽象为组合问题
    • 如何模拟那些切割线
    • 切割问题中递归如何终止
    • 在递归循环中如何截取子串
    • 如何判断回文
    1. class Solution {
    2. public:
    3.     vector> res;
    4.     vector path;
    5.     vectorbool>>isPalindrome; // dp数组
    6.     void backtracking(const string& s, int startIndex){
    7.         if(startIndex >= s.size()){
    8.             res.emplace_back(path);
    9.             return;
    10.         }
    11.         int len = s.size();
    12.         for(int i = startIndex; i < len; ++i){
    13.             if(isPalindrome[startIndex][i]){
    14.                 string str = s.substr(startIndex, i-startIndex+1);
    15.                 path.emplace_back(str);
    16.             }else{
    17.                 continue;
    18.             }
    19.             backtracking(s, i+1);
    20.             path.pop_back();
    21.         }
    22.     }
    23.     void computePalindrome(const string& s){
    24.         int len = s.size();
    25.         isPalindrome.resize(len, vector<bool>(len, false));
    26.         for(int i = len-1; i >= 0; --i){
    27.             for(int j = i; j < len; ++j){
    28.                 if(j == i){
    29.                     isPalindrome[i][j] = true;
    30.                 }else if(j - i == 1){
    31.                     isPalindrome[i][j] = (s[i] == s[j]);
    32.                 }else{
    33.                     isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i+1][j-1]);
    34.                 }
    35.             }
    36.         }
    37.     }
    38.     vector> partition(string s) {
    39.         res.clear();
    40.         path.clear();
    41.         computePalindrome(s);
    42.         backtracking(s, 0);
    43.         return res;
    44.     }
    45. };

  • 相关阅读:
    计算机毕业设计Java小动物领养网站(源码+系统+mysql数据库+Lw文档)
    xml解析技术介绍、使用dom4j读取xml文件内容
    【HTML】【休闲益智】还有9块月饼并未获得!请及时出战!(解锁月饼小游戏
    Reactjs数据篇
    MyBatis foreach标签有什么作用呢?
    Kafka 设计原理
    文章资源下载小程序的基础配置(以Serverless WordPress为后端)
    OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台
    MySQL索引及事物
    2024年软考重大改革
  • 原文地址:https://blog.csdn.net/weixin_42179093/article/details/133901599