LC40. 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 sums to target.
Each number in candidates may only be used once in the combination.

It is very similar to combination sum I. But we have duplicate numbers here.

Duplicates numbers are allowed.

Modify from comb sum I

In the recursive call, we call

    def dfs(self, nums, index, target, path):
        if target < 0:
            return 
        if target == 0:
            self.all_path.append(path)

        for i in range(index, len(nums)):
            self.dfs(nums, i + 1, target - nums[i], path + [nums[i]])

The index is i + 1. That means later recursive call to dfs can only use numbers from index from i + 1 to end.

This makes sure each number is used only once. But can’t avoid duplicate.

EG: Input: candidates = [10,1,2,7,6,1,5], target = 8,
Our algorithm will return two [1,7].
One is the produced by first calling dfs(nums, 1, 7, [1]) then dfs(nums, 3, 0, [1,7]).

The other [1,7] is from dfs(nums, 3, 1, [7]), then dfs(nums, 5, 0, [7, 1]).

Root of the problem

The problem is because we used the same number at the same level. (Assume we sort the candidates first).
EG: Input: candidates = [1,1,7], target = 8,
At the same level of the tree, our previous algorithm will add two paths that starts with 1 to the solution by calling dfs(nums, 1, 7, [1]) and dfs(nums, 2, 7, [1]). This will generate duplicate.

Avoid the problem

Use a dict

We use a dictionary to record the numbers used at each level. And we only recurse when the number is not seen at that level.

Use pre and cur

since the input is sorted. We can just recurse when the previous num[i] is different from current nums[i]

    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        candidates.sort()
        self.dfs(candidates, 0, target, [])
        return self.all_path

    def dfs(self, nums, index, target, path):
        if target < 0:
            return 
        if target == 0:
            self.all_path.append(path)
        prev = None
        for i in range(index, len(nums)):
            cur = nums[i]
            if prev != cur:
                self.dfs(nums, i + 1, target - nums[i], path + [nums[i]])

            prev = cur

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax