Tags: "leetcode", "dfs", "backtracking", access_time 2-min read

Edit this post on Github

Combination Sum II

Created: August 6, 2019 by [lek-tin]

Last updated: March 8, 2020

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

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

Note

All numbers (including target) will be positive integers. The solution set must not contain duplicate combinations.

Example 1

Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

###Example 2:

Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
  [1,2,2],
  [5]
]

Solution

This problem is equivalent to Subsets II because unique combinations means all subsets with no duplicate combinators.

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        # Equivalent to subsets
        results = []
        combination = []
        # Edge cases
        if candidates == None:
            return results
        if len(candidates) == 0:
            results.append([])
            return results
        # Sort candidates in ascending order
        candidates.sort()
        # Start DFS
        self.dfs(0, combination, target, results, candidates)
        return results

    def dfs(self, startIndex, combination, target, results, candidates):
        # Recursion exit condition met
        # Combination with a sum equal to target is found
        if target == 0:
            results.append(combination[:])
            return
        # If startIndex is out of bound, this loop will do nothing.
        for i in range(startIndex, len(candidates)):
            # Avoid duplicate combinator
            # For example, candidates = [1,1,1,2,...]
            # We would only choose [1,1,1], [_,1,1], [_,_,1]
            if i != 0 and i > startIndex and candidates[i] == candidates[i-1]:
                continue
            # Since candidates is in ascending order, if the current number at i is already bigger than target, there is no need to continue. Abort the searching.
            if target < candidates[i]:
                break
            ## Choose current number at i
            combination.append(candidates[i])
            ## Deduct current number at i from target and go one level deeper
            ## Each number in `candidates` may only be used ONCE in the combination, hence i+1
            self.dfs(i+1, combination, target - candidates[i], results, candidates)
            ## Choose current number at i
            combination.pop()