~/problems / Search tricks / Meet in the middle

Basics: subset sums of two halves

easy basics ~10 min

Write two functions:

  1. subset_sums(nums: list[int]) -> list[int]: the sums of all 2^n subsets of nums (chosen by position, so repeats are kept), sorted ascending. The empty subset contributes 0.
  2. has_subset_sum(nums: list[int], target: int) -> bool: is there a subset of nums (possibly empty) whose sum is exactly target?
subset_sums([1, 2, 4])    # [0, 1, 2, 3, 4, 5, 6, 7]
subset_sums([3, 3])       # [0, 3, 3, 6]
subset_sums([])           # [0]
has_subset_sum([5, -2, 9, 4], 7)    # True  (5 + -2 + 4)
has_subset_sum([5, -2, 9, 4], 1)    # False

nums can have up to 36 elements with values up to 10^9 in size (so no DP over sums), and 2^36 subsets is far too many. That's the meet-in-the-middle setting: split nums into two halves, list the 2^18 subset sums of each half with subset_sums, and a subset of the whole list is just one subset from the left half plus one from the right. Put the right half's sums in a set and, for each left sum s, check whether target - s is in it.

Build subset_sums iteratively: start with [0], and for each number x, the new list is the old list plus x added to each old sum.

Constraints: 0 <= len(nums) <= 36 (at most 18 for direct calls to subset_sums), -10^9 <= nums[i], target <= 10^9.

Show hint

sums = [0]; for x in nums: sums += [s + x for s in sums], and has_subset_sum runs that on each half, then does set lookups.

Topic: Meet in the middle. Split n ~ 40 into two halves of 2^20 and combine with sort + two pointers.

0:00
Ctrl ' run · Ctrl ↵ submit
esc