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

 39. 组合总和

该题目关键问题在于 candidates 里面的元素可以复用多次

体现代码关键为:

void backtrack(int[] candidates, int start, int target, int sum) {
    // 回溯算法框架
    for (int i = start; i < candidates.length; i++) {
        // 选择 candidates[i]
        backtrack(candidates, i, target, sum);
        // 撤销选择 candidates[i]
    }
}

而例如 77.组合 当中不能使用重复元素的标准组合代码:

void backtrack(int[] candidates, int start, int target, int sum) {
    // 回溯算法框架
    for (int i = start; i < candidates.length; i++) {
        // 选择 candidates[i]
        backtrack(candidates, i + 1, target, sum);
        // 撤销选择 candidates[i]
    }
}

 

最关键的地方便是递归调用时,不用 i + 1 便表示可以重复读取当前元素

借用卡哥本题的决策树:

 所以完整代码如下:

class Solution {

    List<List<Integer>> res = new LinkedList<>();
    List<Integer> track = new LinkedList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {       
        backtrack(candidates, target, 0, track);
        return res;
    }

    int sum = 0;
    public void backtrack(int[] candidates, int target, int startIndex, List<Integer> track){
        if(sum > target){
            return;
        }

        if(sum == target){
            res.add(new LinkedList<>(track));
            return;
        }

        for(int i = startIndex; i < candidates.length; i++){
            sum += candidates[i];
            track.add(candidates[i]);

            // 该处没有返回 i + 1, 表示重复选取当前元素
            backtrack(candidates, target, i, track);

            sum -= candidates[i];
            track.remove(track.size() - 1);
        }
    }
}

 

40.组合总和II

该题最关键的地方便是去重:集合(数组candidates)有重复元素,但不能有重复的组合

借用卡哥的决策树:(可知,需要在树层去重)

 主要问题便是:如何确定是同一树层上的元素

如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]

如图:

 

    // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
    // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
    // 要对同一树层使用过的元素进行跳过
    if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
        continue;
    }

第二种便是利用 startIndex 来去重:(借用leetcode题解)

 所以完整代码如下:

class Solution {

    List<List<Integer>> res = new LinkedList<>();
    List<Integer> track = new LinkedList<>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrack(candidates, target, 0, 0, track);
        return res;
    }

    public void backtrack(int[] candidates, int target, int startIndex, int sum, List<Integer> track){
        if(sum == target){
            res.add(new LinkedList<>(track));
            return;
        }

        for(int i = startIndex; i < candidates.length; i++){
            if(sum > target){
                return;
            }

            // 去重
            if(i > startIndex && candidates[i] == candidates[i - 1]){
                continue;
            }

            sum += candidates[i];
            track.add(candidates[i]);

            backtrack(candidates, target, i + 1, sum, track);

            sum -= candidates[i];
            track.remove(track.size() - 1);
        }
    }
}

131.分割回文串 

本题的难点:

  • 切割问题如何抽象为组合问题
  • 如何模拟那些切割线
  • 切割问题中递归如何终止
  • 在递归循环中如何截取子串

解决办法:从 s 的头部开始暴力穷举,如果发现 s[0..i] 是一个回文子串,那么我们就可以把 s 切分为 s[0..i] 和 s[i+1..],然后我们去尝试把 s[i+1..] 去暴力切分成多个回文子串即可。 

决策树如下:

 

只有树枝上的子串是回文串时才能继续往下走,最后如果能够走到空串节点,就说明整个 s 完成了切分,也就是得到了一个合法的答案。 

所以完整代码如下:

class Solution {
    
    List<List<String>> res = new LinkedList<>();
    List<String> track = new LinkedList<>();
    public List<List<String>> partition(String s) {
        backtrack(s, 0, track);
        return res;
    }

    public void backtrack(String s, int startIndex, List<String> track){
        if(startIndex == s.length()){
            res.add(new LinkedList<String>(track));
            return;
        }

        for(int i = startIndex; i < s.length(); i++){
            // 判断是否为回文串
            if(!isPalindrome(s, startIndex, i)){
                continue;
            }
            
            // String.substring截取范围 "左闭右开"
            track.add(s.substring(startIndex, i + 1));
            backtrack(s, i + 1, track);
            track.remove(track.size() - 1);
        }
    }

    public boolean isPalindrome(String s,int startIndex, int end){
        while(startIndex < end){
            if(s.charAt(startIndex) != s.charAt(end)){
                return false;
            }
            startIndex++;
            end--;
        }
        return true;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值