[豪の算法奇妙冒险] 代码随想录算法训练营第二十三天 | 39-组合总和、40-组合总和Ⅱ、131-分割回文串

代码随想录算法训练营第二十三天 | 39-组合总和、40-组合总和Ⅱ、131-分割回文串


LeetCode39 组合总和

题目链接:https://leetcode.cn/problems/combination-sum/description/

文章讲解:https://programmercarl.com/0039.组合总和.html

视频讲解:https://www.bilibili.com/video/BV1KT4y1M7HJ/?vd_source=b989f2b109eb3b17e8178154a7de7a51

​ 需要startIndex来控制for循环的起始位置:如果是一个集合来求组合的话,就需要startIndex;如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex

​ 对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历,完成剪枝

image-20251231173914269

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> records = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        backTracking(candidates, target, 0, 0);
        return result;
    }

    public void backTracking(int[] candidates, int target, int sum, int start){
        if(sum == target){
            result.add(new ArrayList<>(records));
            return;
        }
        for(int i = start; i < candidates.length; i++){
            if(sum + candidates[i] > target){
                break;
            }
            records.add(candidates[i]);
            backTracking(candidates, target, sum + candidates[i], i);
            records.remove(records.size()-1);
        }
    }
}

LeetCode40 组合总和Ⅱ

题目链接:https://leetcode.cn/problems/combination-sum-ii/description/

文章讲解:https://programmercarl.com/0040.组合总和II.html

视频讲解:https://www.bilibili.com/video/BV12V4y1V73A/?vd_source=b989f2b109eb3b17e8178154a7de7a51

​ 本题给的集合是有重复元素的,那么求出来的组合有可能重复,但题目要求不能有重复组合,因此需要去重

​ 组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过,因此分为树枝去重和树层去重

​ 本题要求元素在同一个组合内是可以重复的,但两个组合不能相同,所以要树层去重,树枝不用去重

​ 如果 candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明前一个树枝使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1],此时for循环里就应该做continue的操作,做树层的去重

image-20260103152215995

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> records = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        int[] used = new int[candidates.length];
        backTracking(candidates, target, 0, 0, used);
        return result;
    }

    public void backTracking(int[] candidates, int target, int sum, int start, int[] used){
        if(sum == target){
            result.add(new ArrayList<>(records));
            return;
        }
        for(int i = start; i < candidates.length; i++){
            if(i > 0 && candidates[i] == candidates[i-1] && used[i-1] == 0){
                continue;
            }
            if(sum + candidates[i] > target){
                break;
            }
            records.add(candidates[i]);
            used[i] = 1;
            backTracking(candidates, target, sum + candidates[i], i+1, used);
            used[i] = 0;
            records.remove(records.size()-1);
        }
    }
}

LeetCode131 分割回文串

题目链接:https://leetcode.cn/problems/palindrome-partitioning/description/

文章讲解:https://programmercarl.com/0131.分割回文串.html

视频讲解:https://www.bilibili.com/video/BV1c54y1e7k6/?vd_source=b989f2b109eb3b17e8178154a7de7a51

​ 切割问题,也可以抽象为一棵树形结构。递归用来纵向遍历,for循环用来横向遍历,切割线切割到字符串的结尾位置,说明找到了一个切割方法,此时可以发现,切割问题的回溯搜索过程和组合问题的回溯搜索过程是差不多的

​ 本题递归函数参数还需要startIndex作为切割线,切割过的地方,不能重复切割,和组合问题也是保持一致的

​ 在 for (int i = startIndex; i < s.size(); i++) 循环中,我们定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。首先判断这个子串是不是回文,如果是回文,就加入records中,records用来记录切割过的回文子串

​ 判断回文子串,可以使用双指针法,前后往中间遍历

image-20260103162558571

class Solution {
    List<List<String>> result = new ArrayList<>();
    List<String> records = new ArrayList<>();
    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return result;
    }

    public boolean isPalindrome(String str){
        for(int i = 0, j = str.length()-1; i < j; i++, j--){
            if(str.charAt(i) != str.charAt(j)){
                return false;
            }
        }
        return true;
    }

    public void backTracking(String str, int start){
        if(start >= str.length()){
            result.add(new ArrayList<>(records));
            return;
        }
        for(int i = start; i < str.length(); i++){
            String curStr = str.substring(start, i+1);
            if(isPalindrome(curStr)){
                records.add(curStr);
                backTracking(str, i+1);
                records.remove(records.size()-1);
            }
        }
    }
}
posted @ 2026-01-03 16:27  SchwarzShu  阅读(5)  评论(0)    收藏  举报