代码随想录算法训练营 day 27: ● 39. 组合总和 ● 40.组合总和II ● 131.分割回文串

76 阅读2分钟

39. Combination Sum

Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target .  You may return the combinations in any order.

The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the 

frequency

 of at least one of the chosen numbers is different.

The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

 

Example 1:

Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.

Example 2:

Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]

Example 3:

Input: candidates = [2], target = 1
Output: []

 

Constraints:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • All elements of candidates are distinct.
  • 1 <= target <= 40

允许重选元素的回溯。可选元素里没有0,也没有负数。按照一般回溯写就可以。剪枝操作判断若当前和大于target,就没必要继续。使用target递减的方法,可以节省一个形参。

class Solution {
    public void backtracking(int target, int[] candidates, LinkedList<Integer> path, List<List<Integer>> res) {
        if(target < 0) {
            return;
        }

        if(target == 0) {
            res.add(new LinkedList<Integer>(path));
            return;
        }

        for(int i=0; i<candidates.length && (target - candidates[i] >= 0); i++) {
            int cur = candidates[i];
            if(path.size() > 0) {
                int last = path.peekLast();
                if(cur < last) {
                    continue;
                }
            } 

            path.add(candidates[i]);
            backtracking(target - candidates[i], candidates, path, res);
            path.removeLast();
        }
    }
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<List<Integer>>();
        LinkedList<Integer> path = new LinkedList<Integer>();

        if(candidates.length == 0) {
            return res;
        }
        Arrays.sort(candidates);

        backtracking(target, candidates, path, res);

        return res;
    }
}

40. Combination Sum II

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target.

Each number in candidates may only be used once in the combination.

Note: The solution set must not contain duplicate combinations.

 

Example 1:

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

Example 2:

Input: candidates = [2,5,2,1,2], target = 5
Output: 
[
[1,2,2],
[5]
]

 

Constraints:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

这题要思路清晰。关键在于去重。回溯法的调用模式是树状结构,在一层for循环里的递归调用,是同子树递归,而在每一次for循环中,就是同层的关系。 去重的重点是,同子树可以有重复,但同层没有。 那么就将待选元素排序,在当前的for loop里,若当前循环元素与前一个相同,则跳过循环。 同样也可以加入当前和大于target的剪枝操作。

image.png

class Solution {
    public void backtracking(int target, int pos, int[] candidates, LinkedList<Integer> path, List<List<Integer>> res) {
        if(target < 0) {
            return;
        }

        if(target == 0) {
            res.add(new LinkedList<Integer>(path));
            return;
        }

        for(int i=pos; i<candidates.length && (target - candidates[i] >= 0); i++) {
            if(i>0 && i>pos && candidates[i] == candidates[i-1]) {
                continue;
            }
            path.add(candidates[i]);
            backtracking(target - candidates[i], i+1, candidates, path, res);
            path.removeLast();
        }
    }
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<List<Integer>>();
        LinkedList<Integer> path = new LinkedList<Integer>();

        if(candidates.length == 0) {
            return res;
        }

        Arrays.sort(candidates);

        backtracking(target, 0, candidates, path, res);

        return res;
    }
}

131. Palindrome Partitioning

Given a string s, partition s such that every 

substring

 of the partition is a 

palindrome

. Return all possible palindrome partitioning of s.

 

Example 1:

Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]

Example 2:

Input: s = "a"
Output: [["a"]]

 

Constraints:

  • 1 <= s.length <= 16
  • s contains only lowercase English letters.

这题是子串问题。一开始思路有点乱。要抓住几个点

  1. 这题不要把思路集中在一个一个扫字符串上,而应该是切割。以一个位置切割字符串,取左边,判断是否子串。是则递归剩下的,否则返回。
  2. 判断回文没什么捷径,只能单独判断,或者用双指针,或者用dp
  3. 结束条件是扫到字符串末尾为准,写结果返回。
class Solution {
    public boolean isPalindrome(char[] str) {
        if(str.length == 0) {
            return false;
        }

        int left = 0;
        int right = str.length - 1;

        while(left <= right) {
            if(str[left] != str[right]) {
                return false;
            }
            else {
                left++;
                right--;
            }
        }

        return true;
    }

    public void backtracking(LinkedList<String> path, char[] str, int pos,  List<List<String>> res) {
        if(pos >= str.length) {
            res.add(new LinkedList<String>(path));
            return;
        }

        for(int i=pos; i<str.length; i++) {
            char[] left = Arrays.copyOfRange(str, pos, i+1);
            //System.out.println(left);

            if(isPalindrome(left)) {
                path.add(new String(left));
                backtracking(path, str, i+1, res);
                path.removeLast();
            }
        }



    }
    public List<List<String>> partition(String s) {
        List<List<String>> res = new LinkedList<List<String>>();
        LinkedList<String> path = new LinkedList<String>();

        if(s.length() == 0) {
            return res;
        }

        backtracking(path, s.toCharArray(), 0, res);

        return res;
    }
}