Example 1
ready- Input
nums = [ 1, 2, 2 ]- Expected
[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 2, 2 ], [ 1, 2, 2 ] ]- Why
- the two positions holding 2 don't duplicate [2] or [1, 2]
Two identical coupon values shouldn't create two identical coupon bundles. Repeated input values add choices, but they must not duplicate answers.
nums may repeat a value. Build every unique subset — a selection of zero or more
input positions. A value may appear in a subset only as many times as it appears in
nums.
Keep these output rules firm:
[].1 <= nums.length <= 12-100 <= nums[i] <= 100Hints
A bitmask can select positions, but two masks may now produce the same values. Sort each result and store it by an exact tuple key to remove duplicates.
Sort a copy of nums before searching. Equal choices then sit next to each other,
which makes a local skip rule possible.
Inside one recursive loop, ignore a value equal to the previous candidate. Don't
ban it at deeper levels — choosing the second 2 after the first is how [2, 2]
remains possible.
If a value appears k times, a subset can use it zero through k times. Can you use
that copy count to predict the number of unique subsets before generating them?
Visible cases
nums = [
1,
2,
2
][
[],
[
1
],
[
2
],
[
1,
2
],
[
2,
2
],
[
1,
2,
2
]
]nums = [
0,
0
][
[],
[
0
],
[
0,
0
]
]nums = [
-1,
2,
-1
][
[],
[
-1
],
[
-1,
-1
],
[
2
],
[
-1,
2
],
[
-1,
-1,
2
]
]Interview signal
Duplicates turn position-based choices into repeated value-based answers. You can clean them up afterward, or stop duplicate branches before they grow.
Use a bitmask, an integer whose binary digits choose input positions, to generate all
2^n position subsets. Store each sorted subset as a canonical key — one standard
tuple representation used to recognize equivalent answers.
def subsets_with_dup(nums: list[int]) -> list[list[int]]:
values = sorted(nums)
seen: set[tuple[int, ...]] = set()
for mask in range(1 << len(values)):
subset = tuple(
values[i]
for i in range(len(values))
if mask & (1 << i)
)
seen.add(subset)
return [list(subset) for subset in seen]This is hard to get wrong, but repeated values can make it do enormous amounts of work for answers the set immediately throws away.
Time O(n · 2^n). Space O(n · 2^n) in the worst case, including the returned subsets and their deduplication keys.
Backtracking chooses a value, explores below it, then un-chooses it. The partial answers form a solution-space tree, a tree whose branches are candidate choices. After sorting, equal values beside each other would create identical sibling branches — branches leaving the same partial answer — so keep only the first. This is the choose/explore/un-choose backtracking pattern with one duplicate-aware guard.
def subsets_with_dup(nums: list[int]) -> list[list[int]]:
values = sorted(nums)
answer: list[list[int]] = []
path: list[int] = []
def search(start: int) -> None:
answer.append(path.copy())
for i in range(start, len(values)):
if i > start and values[i] == values[i - 1]:
continue
path.append(values[i]) # choose
search(i + 1) # explore
path.pop() # un-choose
search(0)
return answerThe condition i > start is the hinge. It skips a repeated choice only when the earlier
copy was available at this same depth. Once one copy is already in path, recursion
starts deeper, so another equal value may be chosen and [2, 2] survives.
Time O(n · 2^n). Space O(n · 2^n) for the worst-case output, with O(n) auxiliary recursion and path space. Duplicate-heavy inputs visit far fewer branches than the bitmask baseline.
Ready to run.
3 cases are queued.
Visible testcase
Case 1
Expected
[
[],
[
1
],
[
2
],
[
1,
2
],
[
2,
2
],
[
1,
2,
2
]
]