*算法训练(leetcode)第二十天 | 39. 组合总和、40. 组合总和 II、131. 分割回文串

刷题记录

  • [39. 组合总和](#39. 组合总和)
  • [40. 组合总和 II](#40. 组合总和 II)
  • [*131. 分割回文串](#*131. 分割回文串)

39. 组合总和

leetcode题目地址

本题和216. 组合总和 III非常相似(题解)。本题中单个组合中可以无限重复同一个数字,而216. 组合总和 III不可重复,因此在递归时传入下一个位置表示不重复,传入当前位置表示可重复。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

cpp 复制代码
// c++
class Solution {
public:
    int sum = 0;
    vector<int> cur;
    void backtracking(vector<vector<int>> &result, const vector<int>& candidates, int left, int target){
        if(sum == target){
            result.emplace_back(cur);
            return;
        }
        if(left >= candidates.size() || sum>target) return;

        for(int i=left; i<candidates.size(); i++){
            sum += candidates[i];
            cur.emplace_back(candidates[i]);
            backtracking(result, candidates, i, target);

            cur.pop_back();
            sum -= candidates[i];
        }


    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        backtracking(result, candidates, 0, target);
        return result;
    }
};

40. 组合总和 II

leetcode题目地址

由于candidata中有重复数字,故本题允许单个组合内有重复数字(这里的重复数字是在candidate中不同下标相同的元素,不是同一个元素),但不允许有重复组合,也就是说,允许出现[1, 1, 2]的单个组合,但不允许[[1,1,2], [1,1,2]]或[[1,1,2],[1,2,1]]或[[1,1,2],[2,1,1]]这样相同组合出现两次。

若求出所有组合再用set去重会导致时空消耗大。所以需要在寻找答案时就去重。

先将candidate排序,使其中相同元素挨在一起。在回溯结束时进行去重,也就是下标后移。由于重复元素可能不止一个,所以去重用while而不是if。

在写代码的时候我考虑过在for循环开始时去重,这里会有一些问题,手动走一遍就清楚了:

cpp 复制代码
 for(int i=left; i<candidates.size(); i++){
 	// 去重
    while(i>0 && candidates[i] == candidates[i-1]) i++;
    sum += candidates[i];
    cur.emplace_back(candidates[i]);
    backtracking(result, candidates, i+1, target);

    sum -= candidates[i];
    cur.pop_back();
 }

(这里用题目第一个样例举例)

candidates = [10,1,2,7,6,1,5], target = 8

排序过后, candidates = [1,1,2,5,6,7,10],调用回溯算法。

  • left=0,i=0,不进入while循环,sum=1,cur = [1],进入递归。
  • left=1,i=1,进入while循环,i=2,sum=3,cur=[1,2],再进下一层递归。

在这里就可以看出问题了,这种写法会跳过单个组合的重复元素,所以要将去重写在回溯结束之后。

所以针对这一问题进行改进,不难发现上面问题的出现是由于递归开始没有处理元素就跳过了,这样导致组合内的重复、重复的组合都被跳过,但其实要处理的是重复的组合而不是组合内的重复数字,所以将去重条件稍作修改。组合内重复就需要将元素放入组合,也就是将传入的第一个元素放入组合。在回溯结束后当前元素被弹出,当下一个元素和当前元素相同时,就会出现重复的组合,因此将上边的去重代码改为如下条件:

cpp 复制代码
for(int i=left; i<candidates.size(); i++){
 	// 去重写法一 将第一个元素放入组合
    while(i>left && candidates[i] == candidates[i-1]) i++;
    // 去重写法二
    // if(i>left && candidates[i] == candidate[i-1]) continue;
    sum += candidates[i];
    cur.emplace_back(candidates[i]);
    backtracking(result, candidates, i+1, target);

    sum -= candidates[i];
    cur.pop_back();
 }

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

完整代码如下:

cpp 复制代码
// c++
class Solution {
public:
    int sum = 0;
    vector<int> cur;

    void backtracking(vector<vector<int>> &result, const vector<int>& candidates, int left, int target){
        if(sum == target){
            result.emplace_back(cur);
            return;
        }
        if(left >= candidates.size() || sum > target) return;
        for(int i=left; i<candidates.size(); i++){
        	// 去重写法一
            // if(i>left && candidates[i] == candidates[i-1]) continue;
            // 去重写法二
            // while(i>left && candidates[i] == candidates[i-1]) i++;
            sum += candidates[i];
            cur.emplace_back(candidates[i]);
            backtracking(result, candidates, i+1, target);

            sum -= candidates[i];
            cur.pop_back();
            // 去重写法三
            while(i<candidates.size()-1 && candidates[i] == candidates[i+1]) i++;
        }

    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;

        sort(candidates.begin(), candidates.end());
        backtracking(result, candidates, 0, target);
        return result;
    }
};

*131. 分割回文串

leetcode题目地址

本题较为复杂,没有掌握的很熟练。思路来源

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

cpp 复制代码
// c++
class Solution {
public:
    
    vector<string> cur_result;

    bool isPalindrome(const string& s, int left, int right){
        
        if(right == left) return true;
        for(; left<right; left++, right--){
            if(s[left]!=s[right]) return false;
        }
        return true;
    }

    void backtracking(vector<vector<string>> & result, const string& s, int left){
        if(left >= s.size()){
            result.emplace_back(cur_result);
            return;
        }
        for(int i=left; i<s.size(); i++){
            if(isPalindrome(s, left, i)){                
                string cur = s.substr(left, i-left+1);
                cur_result.emplace_back(cur);
            }else continue;
            backtracking(result, s, i+1);
            cur_result.pop_back();
        }
    }

    vector<vector<string>> partition(string s) {
        vector<vector<string>> result;
        backtracking(result, s, 0);
        return result;
    }
};
相关推荐
海绵波波1075 分钟前
Webserver(4.9)本地套接字的通信
c++
@小博的博客11 分钟前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生1 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
爱吃喵的鲤鱼1 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
懒惰才能让科技进步2 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
7年老菜鸡2 小时前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara2 小时前
函数对象笔记
c++·算法
似霰2 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
芊寻(嵌入式)2 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
獨枭2 小时前
C++ 项目中使用 .dll 和 .def 文件的操作指南
c++