DAY27:回溯(组合问题39、40、分割回文串131)

Leetcode: 39 组合总和

基本思路

本题没有组合数字的要求,只有对组合总和的要求,因此返回条件有两个,等于sum的时候收集结果,如果大于sum了就直接跳过。

  • 组合没有数量要求
  • 元素可以重复拾取

这题的难点在于可以反复取值。因此对于回溯的设置需要技巧。本题的关键在于回溯的输入

traceback(candidates, target, sum, i);//关键点:不用i+1了,表示可以重复读取当前的数
class Solution {
private:
    vector<vector<int>> result;
    vector<int> vec;
    void traceback(vector<int>& candidates, int target, int sum, int idx){
        if(target < sum) return;
        if(sum == target){
            result.push_back(vec);
            return;
        }
        for(int i = idx; i < candidates.size(); i++){
            sum += candidates[i];
            vec.push_back(candidates[i]);
            traceback(candidates, target, sum, i);
            sum -= candidates[i];
            vec.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        traceback(candidates, target, 0,0);
        return result;
    }
};

剪枝优化

如果我们事先对candidate进行从小到大的排序的话。当下一层 sum + candidates[i]已经比sum大的时候,就可以提前剪枝了。在求和问题中,先排序再求和是一个常用手段。相关代码如下:

//排序代码
sort(candidates.begin(), candidates.end());
//循环逻辑
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++)

时间复杂度O(n * 2^n)

空间复杂度O(target)

Leetcode: 40 组合总和II

本题与上题的区别在于,元素不能重复使用。没有思路,参考下述题解。

代码随想录

核心的内容是跳过重复的元素。这题一定要先排序,如果一个数层上面元素被重复使用的,就需要跳过。并且这道题必须先对数组进行排序,不然无法进行跳过操作。

// 要对同一树层使用过的元素进行跳过
            if (i > startIndex && candidates[i] == candidates[i - 1]) {
                continue;
            }
class Solution {
private:
    vector<vector<int>> result;
    vector<int> vec;
    void traceback(vector<int>& candidates, int target, int sum, int idx, vector<bool>& used){
        if(sum == target){
            result.push_back(vec);
            return;
        }
        for(int i = idx; i < candidates.size() && sum + candidates[i] <= target; i++){
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
                continue;
            }
            sum += candidates[i];
            vec.push_back(candidates[i]);
            used[i] = true;
            traceback(candidates, target, sum, i + 1, used);
            sum -= candidates[i];
            used[i] = false;
            vec.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool> used(candidates.size(), false);
        sort(candidates.begin(), candidates.end());
        traceback(candidates, target, 0,0,used);
        return result;
    }
};

Leetcode: 131 分割回文串

基本思路

  • 分割问题
  • 判断回文的问题
递归思路
  • 输入:字符串、一个记录分割位置的idx
  • 结束条件:切割位置到达字符末尾;
  • 递归逻辑:判断idx到i的区间内字符串是不是回文,如果是就加入当前的结果,继续向下寻找。回文的判断条件使用双指针,一前一后进行比较。
class Solution {
private:
    vector<vector<string>> result;
    vector<string> vec;
    void traceback(string s, int idx){
        if(idx >= s.size()){//终止条件
            result.push_back(vec);
            return;
        }
        for(int i = idx; i < s.size(); i++){
            if(ishuiwen(s, idx, i)){//如果是回文的话就加入vec
                vec.push_back(s.substr(idx, i - idx +1));
            }
            else continue;//不是就跳过
            traceback(s, i + 1);//进入下一层递归,因为不能重复切割,所以是i+1
            vec.pop_back();
        }
    }
    bool ishuiwen(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) {
        traceback(s , 0);
        return result;

    }
};
困难点
  • 切割问题可以抽象为组合问题
  • 如何模拟那些切割线
  • 切割问题中递归如何终止
  • 在递归循环中如何截取子串
  • 如何判断回文
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值