Divide & Conquer
Split the input in half, enumerate all possibilities for each half independently, then combine using sorting + binary search or a hash map. Reduces O(2^n) to O(2^(n/2)).
You are given an array nums of up to 40 integers and a target goal. Find a subsequence whose sum is as close to goal as possible, and return the minimum absolute difference. This is LeetCode 1755. Closest Subsequence Sum, and it is the classic motivation for meet in the middle.
A subsequence is any subset of the array elements (maintaining order does not matter for sums). An array of length n has subsequences. For each one, you could compute its sum and track the closest to goal. When n = 20, is about a million, which is fine. But when n = 40, is over a trillion. No amount of constant-factor optimization will save you.
The constraint n <= 40 is the signature of meet in the middle. It is too large for full enumeration but exactly twice the size where full enumeration is comfortable. That factor of two is the clue.
Split the array into two halves of roughly equal size, each with about n/2 elements. Enumerate all subset sums for each half independently. That is about a million operations per half when n = 40. Now you have two lists of sums, call them left and right. The total sum of any subsequence of the original array is some element from left plus some element from right (where 0 appears in both lists, representing the empty subset from that half).
The problem reduces to: given two lists and a target, find one element from each list whose sum is closest to the target. Sort one list, then for each element in the other list, binary search for the complement. This combine step takes time.
Consider nums = [5, -7, 3, 5] and goal = 6.
Split: left half = [5, -7], right half = [3, 5].
Enumerate left sums (all subsets of [5, -7]):
{} = 0{5} = 5{-7} = -7{5, -7} = -2So left = [-7, -2, 0, 5] (sorted).
Enumerate right sums (all subsets of [3, 5]):
{} = 0{3} = 3{5} = 5{3, 5} = 8So right = [0, 3, 5, 8].
Combine: For each value r in right, we want the value l in left that makes l + r closest to 6. That means we want l closest to 6 - r.
The answer is 0, achieved by the subsequence {-7, 5, 3, 5} with sum -7 + 5 + 3 + 5 = 6.
from bisect import bisect_left
def minAbsDifference(nums, goal):
n = len(nums)
left_half = nums[:n // 2]
right_half = nums[n // 2:]
def all_sums(arr):
sums = [0]
for x in arr:
sums += [s + x for s in sums]
return sums
left = sorted(all_sums(left_half))
right = all_sums(right_half)
best = float('inf')
for r in right:
target = goal - r
# Binary search for closest value in left
idx = bisect_left(left, target)
if idx < len(left):
best = min(best, abs(left[idx] - target))
if idx > 0:
best = min(best, abs(left[idx - 1] - target))
return bestThe all_sums function generates all subset sums of an array of length k by iteratively including or excluding each element. Sorting left enables binary search during the combine step.
Each half has at most n/2 elements, so each produces at most sums. Generating them takes time. Sorting the left list takes . The combine step does binary searches, each costing , for a total of .
Overall: time and space. For n = 40, is about 40 million, well within time limits. Compare this to for the brute force. The exponential base is the same, but the exponent has been halved, which is an enormous practical improvement.
LeetCode 454. 4Sum II gives you four arrays A, B, C, D each of length n. Count the number of tuples (i, j, k, l) such that A[i] + B[j] + C[k] + D[l] = 0.
The brute force iterates over all tuples. Meet in the middle splits the four arrays into two pairs: compute all pairwise sums A[i] + B[j] and all pairwise sums C[k] + D[l]. For each sum from the first pair, you need the count of the negation in the second pair. A hash map makes this per lookup.
from collections import Counter
def fourSumCount(nums1, nums2, nums3, nums4):
ab_sums = Counter(a + b for a in nums1 for b in nums2)
count = 0
for c in nums3:
for d in nums4:
count += ab_sums[-(c + d)]
return countThis runs in time and space, a massive improvement over . The meet-in-the-middle structure is the same: split the problem in half, enumerate all combinations within each half, then combine using a fast lookup. Here the "halves" are two pairs of arrays, the "enumeration" is computing pairwise sums, and the "combine" is a hash map lookup instead of binary search.
The telltale signs:
For problems with four independent dimensions (like 4Sum II), n can be up to 200 because per half is manageable and the combine step is also .
LeetCode 805. Split Array With Same Average asks whether an array can be partitioned into two non-empty parts with equal averages. After algebraic reduction, this becomes: find a non-empty proper subset whose sum equals a specific target (proportional to the subset size). The array can have up to 30 elements, making too slow but feasible. Meet in the middle splits the array in half, enumerates subset sums (grouped by subset size) for each half, and checks whether any combination from the two halves hits the target. The grouping by size adds a layer of bookkeeping but the core split-enumerate-combine structure is identical.
Meet in the middle is a general technique for cutting an exponential search space in half. Split the input into two parts, enumerate all possibilities for each part independently, then combine the two halves using an efficient lookup structure. The time complexity drops from to , which transforms problems that would take centuries into ones that run in seconds. Look for it whenever n is in the 30-40 range and the problem involves subsets or combinations.
You're probably looking at meet in the middle when:
Common templates: