Given an integer array nums that may contain duplicates, return all possible subsets (the power set).
The solution set must not contain duplicate subsets. Return the solution in any order.
Using extra space.
- we use a set here to remember the subsets that we’ve seen already.
- we sort because leetcode expects the actual subsets to be in order.
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
ans = [[]]
seen = set()
nums.sort()
def backtrack(idx, acc):
if idx < len(nums):
# don't take the current number
backtrack(idx+1, acc)
# take the current number
backtrack(idx+1, acc[:]+[nums[idx]])
new = "".join(map(str, acc[:]+[nums[idx]]))
if new not in seen:
ans.append(acc[:]+[nums[idx]])
seen.add(new)
backtrack(0, [])
return ansWithout extra space.
Instead of keeping a set to store what we’ve seen, we just store the duplicates in “blocks”, and then we can recurse on every possible number of them at once instead of risking the chance of using the same number of a duplicate in two recursive branches.
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
"""
- the only way we get duplicates are when there are multiples
of the same number, and we can pick some of them.
- if we sort the array, we can think of these duplicates as blocks.
- then, in one recursive iteration, we can pick 0-n of these
duplicates in a block and recurse.
"""
ans = [[]]
nums.sort()
blocks = []
cur = None
for num in nums:
if cur is None:
cur = num
count = 1
elif cur == num:
count += 1
else:
blocks.append((cur, count))
cur = num
count = 1
blocks.append((cur, count))
def backtrack(idx, acc):
if idx < len(blocks):
val, count = blocks[idx]
for cnt in range(count+1):
# take the current number cnt times
backtrack(idx+1, acc[:]+cnt*[val])
if cnt > 0:
ans.append(acc[:]+cnt*[val])
backtrack(0, [])
return ansHere’s another elegant way to do it without the block construction.
The way we append “intermediate” values of acc to our res kind of reminds me of 254-factor-combinations.
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums.sort()
res = []
acc = []
# recursive call tries to fill the current idx in subset
# with every distinct value...
def recurse(idx):
res.append(acc[:])
for i in range(idx, len(nums)):
# this is why we skip duplicates
if i > idx and nums[i] == nums[i-1]:
continue
acc.append(nums[i])
recurse(i+1)
acc.pop()
recurse(0)
return res