首页 > 编程语言 >代码随想录算法训练营第二十六天| 39. 组合总和 40.组合总和II 131.分割回文串

代码随想录算法训练营第二十六天| 39. 组合总和 40.组合总和II 131.分割回文串

时间:2024-02-23 19:35:03浏览次数:32  
标签:target 组合 int 随想录 start vector candidates path 总和

组合总和

题目链接:39. 组合总和 - 力扣(LeetCode)

思路:依然一是套用回溯模板,但是我们这里用回溯的是i而不是i+1,因为元素可以重复使用,注意for循环里if(sum(path)<=target)的等号不能少。

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    int sum(vector<int> path){
        int s=0;
        for(auto i:path)s+=i;
        return s;
    }
    void backtracking(vector<int> c,int target,int start){
        if(sum(path)==target){
            result.push_back(path);
        }else{
            for(int i=start;i<c.size();i++){
                path.push_back(c[i]);
                if(sum(path)<=target){
                backtracking(c,target,i);
                path.pop_back();
                }
                else{
                path.pop_back();
                }
            }
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates,target,0);
        return result;
    }
};

但事实上,我们可以利用递归特性来完成总和计算,额外写一个sum函数来计算总和会极大降低我们的效率,尽管思路一致,上下两种写法的速度差了很多。

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> state;              // 状态(子集)
        sort(candidates.begin(), candidates.end()); // 对 candidates 进行排序
        int start = 0;                  // 遍历起始点
        vector<vector<int>> res;        // 结果列表(子集列表)
        backtrack(state, target, candidates, start, res);
        return res;
    }
private:
    void backtrack(vector<int> &state, int target, vector<int> &choices, int start, vector<vector<int>> &res) {
        // 子集和等于 target 时,记录解
        if (target == 0) {
            res.push_back(state);
            return;
        }
        // 遍历所有选择
        // 剪枝二:从 start 开始遍历,避免生成重复子集
        for (int i = start; i < choices.size(); i++) {
            // 剪枝一:若子集和超过 target ,则直接结束循环
            // 这是因为数组已排序,后边元素更大,子集和一定超过 target
            if (target - choices[i] < 0) {
                break;
            }
            // 尝试:做出选择,更新 target, start
            state.push_back(choices[i]);
            // 进行下一轮选择
            backtrack(state, target - choices[i], choices, i, res);
            // 回退:撤销选择,恢复到之前的状态
            state.pop_back();
        }
    }
};

作者:Krahets
链接:https://leetcode.cn/problems/combination-sum/solutions/2363929/39-zu-he-zong-he-hui-su-qing-xi-tu-jie-b-9zx7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

组合总和II

题目链接:40. 组合总和 II - 力扣(LeetCode)

思路:本来以为是一个简单的回溯题,直到我遇到了

看来这道题对剪枝的要求不低啊。下面的做法就被这个用例卡了。

class Solution {
public:
    vector<vector<int>>result;
    vector<int>path;
    map<vector<int>,int>m;
    void backtracking(vector<int>candidates,int target,int start){
        if(target==0){
            if(!m[path]){
            result.push_back(path);
            ++m[path];
            }
        }else{
            for(int i=start;i<candidates.size();i++){
                path.push_back(candidates[i]);
                if(target-candidates[i]>=0)
                {backtracking(candidates,target-candidates[i],i+1);
                path.pop_back();
                }else{
                    path.pop_back();
                }
            }
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        int sum=0;
        for(auto i:candidates)sum+=i;
        if(sum<target)return result;        //剪枝
        sort(candidates.begin(),candidates.end());
        backtracking(candidates,target,0);
        return result;
    }
};

下面看一个剪枝剪得很巧妙的解法,该解法规避了对重复元素的反复递归,因为它将数组排序后,如果发现重复选取同一个数字,则直接结束本轮循环,但本题的答案中又有可能出现相同的元素。其实并不矛盾,因为这个解法里每一次递归后,start发生变化,即使元素重复,但第一个元素又能取到了,因此仍能完成任务。

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<int> state;              // 状态(子集)
        sort(candidates.begin(), candidates.end()); // 对 candidates 进行排序
        int start = 0;                  // 遍历起始点
        vector<vector<int>> res;        // 结果列表(子集列表)
        backtrack(state, target, candidates, start, res);
        return res;
    }
private:
    void backtrack(vector<int> &state, int target, vector<int> &choices, int start, vector<vector<int>> &res) {
        // 子集和等于 target 时,记录解
        if (target == 0) {
            res.push_back(state);
            return;
        }
        // 遍历所有选择
        // 剪枝二:从 start 开始遍历,避免生成重复子集
        // 剪枝三:从 start 开始遍历,避免重复选择同一元素
        for (int i = start; i < choices.size(); i++) {
            // 剪枝一:若子集和超过 target ,则直接结束循环
            // 这是因为数组已排序,后边元素更大,子集和一定超过 target
            if (target - choices[i] < 0) {
                break;
            }
            // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过
            if (i > start && choices[i] == choices[i - 1]) {
                continue;
            }
            // 尝试:做出选择,更新 target, start
            state.push_back(choices[i]);
            // 进行下一轮选择
            backtrack(state, target - choices[i], choices, i + 1, res);
            // 回退:撤销选择,恢复到之前的状态
            state.pop_back();
        }
    }
};

作者:Krahets
链接:https://leetcode.cn/problems/combination-sum-ii/solutions/2363941/40-zu-he-zong-he-iihui-su-qing-xi-tu-jie-7y8s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

分割回文串

题目链接:131. 分割回文串 - 力扣(LeetCode)

思路:本题投降代码随想录 (programmercarl.com)

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) {
        result.clear();
        path.clear();
        backtracking(s, 0);
        return result;
    }
};

 

标签:target,组合,int,随想录,start,vector,candidates,path,总和
From: https://www.cnblogs.com/Liubox/p/18029860

相关文章

  • java面向对象之封装-抽象-继承-组合-多态五种概念一网打尽
    说明曾经在学习java面向对象时,你是否会为面向对象的封装-继承-抽象-多态-组合等各种概念搞得稀里糊涂,乃至反复阅读,背诵其相关概念,结果一段时间过后又还给了时间。。。这种经历简直令人发指,让人无法忍受,难道就没有哪个地方能把它一次说清楚,老百姓看了以后纷纷醍醐灌顶,不再重蹈覆......
  • 代码随想录:数组
    二分查找二分查找是对数组中的区间进行查找。有两种写法:一种是在闭区间内进行查找,另一种是在左闭右开区间内进行查找。每次查找的时候只有按照这个方法就不会出错。二分查找写法一:classSolution{public:intsearch(vector<int>&nums,inttarget){intle......
  • day41 动态规划part3 代码随想录算法训练营 96. 不同的二叉搜索树
    题目:96.不同的二叉搜索树我的感悟:这题,考的概率不大,听一遍,过一遍就行。理解难点:二叉搜索树定义为什么是累加的听课笔记:代码示例:classSolution:defnumTrees(self,n:int)->int:dp=[0]*(n+1)#创建一个长度为n+1的数组,初始化为0d......
  • day40 动态规划part3 代码随想录算法训练营 343. 整数拆分
    题目:343.整数拆分我的感悟:题目很难,但我动力十足!!理解难点:如何拆分为什么要保留dp[i]听课笔记:代码示例:classSolution:defintegerBreak(self,n:int)->int:#思路:#dp[i]是到目前为止能拆分取的最大值#dp[i]可以拆成j*(集合)......
  • 代码随想录 day58 判断子序列 不同的子序列
    判断子序列dp[i][j]表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。if(s[i-1]==t[j-1])t中找到了一个字符在s中也出现了if(s[i-1]!=t[j-1])相当于t要删除元素,继续匹配不同的子序列dp[i][j]:以i-1为结尾的s子序列中......
  • 代码随想录算法训练营day02 | leetcode 977. 有序数组的平方、35.搜索插入位置、34.在
    题目链接:977.有序数组的平方-简单题目描述:给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。示例1:输入:nums=[-4,-1,0,3,10]输出:[0,1,9,16,100]解释:平方后,数组变为[16,1,0,9,100]排序后,数组变为[0,1,9,16,100]......
  • 代码随想录算法训练营第二十五天| 216.组合总和III 17.电话号码的字母组合
    组合总和III题目链接:216.组合总和III-力扣(LeetCode)思路:仿照昨天的递归模板写的,同样是for循环横向遍历,递归纵向遍历。注意当k>n时要直接跳出,否则会判断栈溢出。额外发现一个问题就是在累加sum时,用for(autoi:path)sum+=path[i];会出现奇怪数字,原因是auto遍历用法错误,正确写......
  • day39 动态规划part2 代码随想录算法训练营 63. 不同路径 II
    题目:63.不同路径II我的感悟:题目不难,就是不知道哪个煞笔,把路拦截死了,并且入口就放石头,我真是吐了。理解难点:初始值的遇到障碍要Break其他我写的没错边界考虑:还有入口和出口有障碍物的话,要直接返回0.听课笔记:差不多,考虑的点就是:初始值后面为break开头和结尾有障......
  • 代码随想录算法训练营第二十五天 | 17.电话号码的字母组合 , 216.组合总和III
    216.组合总和III 已解答中等 相关标签相关企业 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:只使用数字1到9每个数字 最多使用一次返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回......
  • 代码随想录算法训练营day 1 | 704 二分查找 27 删除元素
    704二分查找数组基础数组空间地址连续、随机访问时间复杂度O(1)、删除和移动时间复杂度O(n)vector和array区别:vector底层实现为array;array是栈上开辟空间、vector是堆上开辟空间;array不支持迭代器访问,支持指针和索引、vector还支持迭代器访问二分查找适用场景有序数组、数组......