LC Patterns — Combinatorics
The Master Insight (read before the patterns)
Combinatorics problems ask "how many?" and the losing move is to generate. Every pattern here is a way to count a set without listing it: decompose it into independent choices (multiply), split it into disjoint cases (add), map it onto a set you already know how to count (bijection), or count each element's contribution instead of each object (linearity).
The four counting moves, in the order you should try them:
- Multiply independent stages — if a choice at stage i never constrains stage j, ways = ∏ (options per stage).
- Add disjoint cases — partition the objects by some feature; count each class; sum.
- Bijection — "each X corresponds to exactly one Y" where Y is countable (paths ↔ letter shuffles, trees ↔ parenthesizations). State the bijection out loud; it is the proof.
- Linearity / contribution flip — instead of Σ over objects of f(object), compute Σ over elements of (number of objects containing it) × (element's contribution). Turns exponential sums into one pass.
And the two repair tools when a direct count overshoots: divide by symmetry (each object counted k times ⇒ divide by k) and inclusion–exclusion (subtract overlaps with alternating signs).
The interview habit that matters most here: verify every formula against brute force on n = 2, 3 before trusting it — a bijection that's off by one is confidently, silently wrong. Say "let me sanity-check against the n=3 case by hand" — it reads as rigor, not doubt.
§0Mechanics & Universal Pitfalls
Computing C(n, k) — pick by situation
import math
math.comb(n, k) # exact, no mod — one-shot, moderate n
# Under mod, many queries: precompute once —
MOD = 10**9 + 7
fact = [1] * (N + 1)
for i in range(1, N + 1): fact[i] = fact[i-1] * i % MOD
inv_fact = [1] * (N + 1)
inv_fact[N] = pow(fact[N], MOD - 2, MOD)
for i in range(N, 0, -1): inv_fact[i-1] = inv_fact[i] * i % MOD
def C(n, k):
if k < 0 or k > n: return 0
return fact[n] * inv_fact[k] % MOD * inv_fact[n-k] % MODThird option — Pascal's row in O(k) via the ratio C(n, k+1) = C(n, k) · (n−k) // (k+1): multiply before dividing and it stays exact in integers (LC 119's intended trick).
Identities worth having on tap
- Symmetry
C(n,k) = C(n,n−k); PascalC(n,k) = C(n−1,k−1) + C(n−1,k). - Interleaving: ways to merge two sequences of lengths m, n preserving internal order =
C(m+n, m). Quietly powers half of this file. - Subsets:
Σₖ C(n,k) = 2ⁿ; permutationsP(n,k) = n!/(n−k)!; multinomialn! / (a!·b!·c!). - Catalan:
Cat(n) = C(2n,n)/(n+1), recurrenceCat(n+1) = Σ Cat(i)·Cat(n−i); first values 1, 1, 2, 5, 14, 42. - Stars and bars: non-negative solutions of
x₁+…+xₖ = nisC(n+k−1, k−1).
Universal pitfalls
- Ordered vs unordered — pairs (i, j): is (j, i) the same object?
C(k,2)vsk(k−1). Decide before counting; LC 1512 vs 447 differ exactly here. - Overcounting without noticing — every "multiply the choices" needs the independence sentence; if stages constrain each other, the product is wrong.
- Division under mod without a modular inverse — and
C(n,k)must return 0 for k < 0 or k > n rather than crash. - Formula ≠ DP confusion — if bounds are small (n ≤ 1000), DP is safer under pressure and equally accepted. Say which you're choosing and why.
- Skipping the empty case —
2ⁿcounts the empty subset; problems often exclude it (−1 at the end).
1Multiplication & Addition Principles (independent stages)
What it is: The answer decomposes into a sequence of independent decisions — count options per stage and multiply; where the structure branches into disjoint shapes, count each and add. The rest of combinatorics is this pattern with better costumes.
Intuition: good numbers (1922): each even index independently takes 5 values, each odd index 4 → 5^⌈n/2⌉ · 4^⌊n/2⌋ — huge n, so fast power finishes it. Unique digits (357): build numbers digit by digit — first digit 9 options (no 0), second 9 (0 allowed, one used), then 8, 7, … — a falling product summed over lengths. Pickup/delivery (1359): the i-th pair drops into 2i−1 gaps × i orderings → ∏ i(2i−1), discovered by hand-computing n = 1, 2.
Approach: narrate stages, verify independence ("the choice at position i doesn't restrict position j because…"), write the product, compute with a loop + mod (or fast power when stages repeat). When the count varies by a feature (length, first digit), sum over that feature's disjoint classes. Small-case verification is non-negotiable.
Challenges & pitfalls
false independence (counting strings "without repeated letters" as 26ⁿ); forgetting to sum disjoint cases; recognizing the repeated-stage → fast-power upgrade when n reaches 10¹⁵; presenting the product without the independence sentence.
[0, 10ⁿ) with all-distinct digits, for n = 2.- Sum over disjoint lengths (addition principle), multiplying within each length:
- length 0 → just the number
0: 1. - length 1 (
1..9): 9 choices. - length 2: first digit 9 options (no leading 0), second digit 9 options (0 now allowed, but one digit already used) →
9 × 9 = 81. - Total
1 + 9 + 81 =91. ✓ Within a length the digit choices are independent (each just excludes the used ones); across lengths the cases are disjoint, so you add.
2Binomial Coefficients & Lattice Paths (choosing positions)
What it is: The object is determined by which positions get which of two labels — grid paths (which steps go right), step sequences (which steps go +1) — so the count is a single C(n, k).
Intuition: unique paths (62): any monotone path in an m×n grid is a string of (m−1) D's and (n−1) R's; the path is the choice of R-positions → C(m+n−2, m−1). Reach position after k steps (2400): to land displacement d in k unit steps, you need (k+d)/2 rights → C(k, (k+d)/2), and 0 when k+d is odd or |d| > k. Pascal's row (119): generate row n in O(k) by the running-ratio identity.
Approach: find the two-label description ("each object ↔ a choice of which k of n slots are X"); write C(n, k); guard impossible parities/ranges with 0. For the DP-vs-formula fork on 62: both accepted; formula shows insight, DP generalizes to obstacles.
Challenges & pitfalls
off-by-one in the bijection (m+n−2, not m+n); parity guards forgotten in 2400 (returns garbage instead of 0); ratio-method integer division order; when obstacles appear, the formula dies and DP takes over.
k = 3 unit steps (±1 each).- Net displacement
d = 2 − 1 = 1. In 3 steps, ifrare rights andllefts,r + l = 3andr − l = 1→r = 2. - The object is "which 2 of the 3 steps are rights" →
C(3, 2) =3. ✓ The guards matter: ifk + dwere odd (unreachable parity) or|d| > k, the answer would be 0 — herek + d = 4is even and reachable.
3Interleavings & Multinomials (merge counts, preserve order)
What it is: Count ways to merge sequences whose internal order is fixed — shuffle two subtree orders, schedule tasks respecting per-chain order. The atom: merging lengths m and n = C(m+n, m); merging many groups = the multinomial (Σsizes)! / ∏(sizeᵢ!).
Intuition: the merged sequence is determined by which slots each group occupies (its internal order fills them uniquely). Same-BST reorderings (1569): the root must come first; left- and right-subtree elements interleave freely → ways(root) = C(L+R, L) · ways(left) · ways(right). Build rooms (1916): topological orderings of a tree = n! / ∏(subtree_size(v)).
Approach: identify the fixed-order chains/subtrees; count interleavings top-down: at each combine point multiply children's counts by the binomial distributing slots. Precompute fact/inv_fact to make every C O(1). Verify on a 3-node tree by hand.
Challenges & pitfalls
believing internal order is actually fixed (in 1569, the recursion handles within-subtree order — don't double-count); the −1 for excluding the identity arrangement (1569); subtree sizes vs node counts off-by-one in 1916; the meta-recognition — "relative order within groups fixed, groups shuffle" ⇒ C(m+n, m).
[2, 1, 3] build the same BST (excluding the original)?- Inserting
[2, 1, 3]makes root 2, left subtree{1}, right subtree{3}. To rebuild the same tree,2must come first, but the left element1and right element3can interleave in any order among the remaining 2 slots. - Ways =
C(L+R, L) · ways(left) · ways(right) = C(1+1, 1) · 1 · 1 = 2total orderings ([2,1,3]and[2,3,1]). Excluding the original →2 − 1 =1. ✓ The binomialC(2,1)counts how the two independent subtree streams interleave.
4Catalan Numbers (balanced / non-crossing structures)
What it is: One number sequence — 1, 1, 2, 5, 14, 42, 132… — counts a shocking family of objects: balanced parentheses, distinct BSTs, full binary trees, monotone paths staying below the diagonal, non-crossing chords. Recognize the shape, name Catalan, and the problem is done.
Intuition: the recurrence is a first-decision split: fix the first structural choice (the root; the match of the first '('), and the object splits into two smaller independent instances → Cat(n+1) = Σᵢ Cat(i)·Cat(n−i). Unique BSTs (96): choose root k → left subtree from k−1 keys, right from n−k → exactly the recurrence. Generate parentheses (22): the count is Cat(n) — a strong moment to answer "42 for n=5" mid-backtracking.
Approach: small n (all these problems): DP the convolution in O(n²) — clean and mod-safe. Closed form C(2n,n)/(n+1) (or C(2n,n) − C(2n,n+1) — subtraction form avoids inverses) when n is large.
Challenges & pitfalls
recognizing Catalan in costume (trigger words: balanced, non-crossing, valid nesting, distinct shapes — memorize 1, 2, 5, 14); the recurrence's index gymnastics (left size i, right size n−1−i); 894 parity (even n → 0 trees); dividing by n+1 under mod without an inverse.
1..n, for n = 3?- Condition on the root: root
kusesk−1keys on the left,n−kon the right, and the two sides are independent → multiply, sum overk. Cat(0)=1, Cat(1)=1, Cat(2)=2.Cat(3) = Cat(0)·Cat(2) + Cat(1)·Cat(1) + Cat(2)·Cat(0) = 2 + 1 + 2 =5.- Answer 5 — the 3rd Catalan number. ✓ Seeing the sequence 1, 1, 2, 5 emerge from your small cases is the signal to name Catalan.
5Group, Then Choose (hashmap + C(k, 2))
What it is: Count pairs (or triples) of "equivalent" items — equal values, same ratio, same distance. Group items by an equivalence key with a hashmap; each group of size k contributes C(k,2) unordered or k(k−1) ordered pairs; sum over groups.
Intuition: a pair is "good" iff both members share some computable signature — so pair-counting collapses to key design plus one identity. Good pairs (1512): key = value, Σ C(k,2). Interchangeable rectangles (2001): key = reduced ratio (w//g, h//g) — never a float. Boomerangs (447): per anchor, key = squared distance; ordered pairs → Σ k(k−1). Streaming refinement: for each new item, answer += count[key] then increment — pairs counted once with i < j for free.
Approach: design the canonical key (reduce fractions by gcd; sort tuple components when order shouldn't matter; squared distances to stay integer); one pass to group (Counter); one pass over group sizes applying C(k,2) / k(k−1).
Challenges & pitfalls
ordered vs unordered (447 is ordered! — read the object's definition); non-canonical keys splitting a group; add-then-increment order in streaming form; per-anchor maps leaking across anchors in 447; saying the identity's story ("k items, each unordered pair once: k choose 2").
nums = [1, 2, 3, 1, 1, 3]; count pairs (i, j) with i < j and nums[i] == nums[j].- Group by value:
1appears 3 times,3appears 2 times,2once. - Each group of size k gives
C(k, 2)unordered pairs: value 1 →C(3,2) = 3; value 3 →C(2,2) = 1; value 2 → 0. - Total
3 + 1 =4. ✓ Grouping turns an O(n²) pair scan into "count each equivalence class, apply one identity." Becausei < jis unordered, it'sC(k,2), notk(k−1).
6Contribution Counting (flip the sum) ★
What it is: Asked for a sum over an exponential family — all subsequences, all subarrays — flip it: for each element (or pair), count how many family members it appears in (with what role), multiply, and sum. Linearity turns 2ⁿ objects into n terms.
Intuition: subsequence widths (891): Σ(max−min) over 2ⁿ subsequences — but sort (widths don't care about order) and element a[i] is the max of exactly 2^i subsequences and the min of 2^(n−1−i) → Σ a[i]·(2^i − 2^(n−1−i)). Subarray minimums (907): element a[i] is the minimum of left · right subarrays, where left/right = distances to the previous-smaller and next-smaller elements (a monotonic stack); contribution a[i]·left·right.
Approach: three steps: (1) reorder/normalize if the objective permits (sorting kills order-dependence — state why it's legal); (2) for each element, derive "in how many objects does it play role R?" — usually a power of 2 or a product of spans; (3) one pass with precomputed powers of 2 mod p. The tie-break in 907: strict-less on one side, ≤ on the other, so equal minimums count once.
Challenges & pitfalls
the flip itself ("sum over objects → sum over elements × multiplicity"); justifying the sort (legal when the statistic is order-free); duplicates double-counted in span products (the strict/non-strict convention); powers of 2 recomputed with pow per element instead of a prefix table.
arr = [3, 1, 2, 4]; sum the minimum of every contiguous subarray.- Flip it: instead of listing subarrays, ask each element "for how many subarrays am I the minimum?" That's
left · right, whereleft= how far you can extend leftward before a smaller element,right= rightward. 3(idx 0): smaller element immediately at idx 1 →left=1, right=1→ contributes3·1·1 = 3.1(idx 1): nothing smaller either side →left=2, right=3→1·2·3 = 6.2(idx 2): smaller1on the left →left=1, right=2→2·1·2 = 4.4(idx 3): smaller2on the left →left=1, right=1→4·1·1 = 4.- Total
3 + 6 + 4 + 4 =17. ✓ Counting each element's contribution (via a monotonic stack for the spans) replaces an O(n²) enumeration of subarrays.
7Inclusion–Exclusion (subtract the overlaps, alternate the signs)
What it is: Count "divisible by any of…" / "having at least one of…" where the easy counts overlap: add singles, subtract pairwise intersections, add back triples — signs alternating by subset parity. On LC the intersections are almost always LCMs.
Intuition: |A ∪ B ∪ C| = Σ|single| − Σ|pair| + |triple| — each element counted once no matter how many sets it's in. Multiples ≤ x of a: x // a; of both a and b: x // lcm(a,b) — lcm, never a·b. Nth magical (878): 2 sets + monotone count → binary search on the answer. Kth smallest coin amount (3116): up to 15 coins → iterate all 2¹⁵ subsets, sign = subset-size parity, term = x // lcm(subset).
Approach: template for the general case: for mask in 1..2^m − 1: compute l = lcm of chosen, early-break if l > x; count += (+1 if popcount odd else −1) * (x // l). Wrap in a boundary-search when the ask is "k-th" rather than "how many ≤ x". Sanity-check count(x) against a brute list for x ≤ 50.
Challenges & pitfalls
product-instead-of-lcm (it's that common); sign errors past 3 sets (popcount parity); lcm overflow without the early break; duplicated divisors in input (dedupe first); keeping the counting layer and the k-th-element (binary search) layer cleanly separated.
[1, n] divisible by 3, 5, or 7, for n = 7.- The numbers ≤ 7 divisible by any of them:
3, 5, 6(div by 3),5(div by 5),7(div by 7). Distinct set{3, 5, 6, 7}, sum = 21. - Inclusion–exclusion view: (multiples of 3: 3+6=9) + (of 5: 5) + (of 7: 7) − (of 15, 21, 35 — all > 7, so 0) + (of 105 — 0) =
9 + 5 + 7 = 21. ✓ The overlap terms uselcm(15, 21, 35), which here exceed n and vanish — but using3·5 = 15vslcm(3,5) = 15happens to match only because 3 and 5 are coprime; for 4 and 6 you'd needlcm = 12, not 24.
8Combinatorial Unranking (build the k-th object, no enumeration)
What it is: "Return the k-th permutation/instruction-string/happy-string" with k up to billions: construct the answer position by position — at each slot, count how many objects start with each candidate prefix, and walk past whole blocks until k lands inside one.
Intuition: objects in sorted order cluster into blocks by first choice, and block sizes are computable: permutations starting with a fixed first element — (n−1)!; instruction strings starting with 'H' — C(remaining−1, remaining V's); happy strings with a fixed first char — 2^(n−1). So the k-th object's first symbol is found by subtracting block sizes from k until it fits, then recurse on the suffix. This is positional notation generalized (60 is writing k in factorial base).
Approach: convert to 0-indexed k first (k -= 1 — do it once, immediately). Per position: for each candidate symbol in sorted order, compute block = count of completions; if k >= block: k -= block else commit the symbol and descend. For 60, maintain the shrinking list of unused digits.
Challenges & pitfalls
the 1-vs-0 index conversion (the bug of this pattern); block-count formulas under constraints; mutable candidate sets in 60 (index into remaining digits); explaining the skip-blocks idea cleanly.
k = 3rd permutation of 1, 2, 3 in sorted order.- Convert to 0-indexed:
k = 2. Digits available:[1, 2, 3]. - Position 1: each first digit heads a block of
(3−1)! = 2permutations. Block index =2 // 2 = 1→ pickdigits[1] = 2; newk = 2 % 2 = 0; remaining[1, 3]. - Position 2: block size
(2−1)! = 1. Index =0 // 1 = 0→ pickdigits[0] = 1;k = 0; remaining[3]. - Position 3: pick
3. - Result "213". ✓ Check: sorted permutations are 123, 132, 213, 231, 312, 321 — the 3rd is 213. Skipping whole
(n−1)!blocks avoids ever generating the earlier permutations.
9Counting in Digit / Prefix Trees (count numbers ≤ N with structure)
What it is: Count or rank numbers below a bound by their decimal structure — how many contain only certain digits, how many 1s appear across all numbers, the k-th number in lexicographic order. Model numbers as paths in a 10-ary trie; count whole subtrees, walk the boundary.
Intuition: numbers ≤ N with digits from a set (902): count by length — for lengths < len(N), free choice |D|^len; for length == len(N), walk N's digits left to right, at each position adding the count of allowed digits strictly below N[i] (each opens a free suffix), staying tight on an equal digit. K-th lexicographic number (440): the trie ordering is preorder; steps(prefix) = size of the prefix's subtree clipped to ≤ N; if k ≥ steps, move to the next sibling, else descend — unranking on the digit trie.
Approach: draw the 10-ary trie once — 902 counts leaves under allowed branches; 233 counts labeled edges; 440 navigates preorder with subtree sizes. Keep the two states of any walk explicit: tight (prefix equals N's prefix) vs free (already smaller).
Challenges & pitfalls
the tight/free distinction evaporating mid-code (name the variables); 440's clip min(next, N+1) off-by-one; 233's current-digit case analysis (derive live from examples); recognizing when constraints exceed formula reach → graduate to digit DP.
n = 13, find the k = 2nd number in lexicographic (dictionary) order.- Lexicographic order of
1..13is:1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9(because "10" sorts right after "1"). - Navigate the digit trie in preorder: start at prefix
1. Its subtree (1, 10, 11, 12, 13) has 5 numbers ≤ 13. Sincek = 2 ≤ 5, the answer is inside — step 1 lands on1itself, so descend to10for step 2. - Answer 10. ✓ Counting a prefix's subtree size (clipped to ≤ n) lets you skip or enter whole branches instead of listing numbers — essential when n is up to 10⁹.
▤Summary Table — Recognition Cheat Sheet
| # | Pattern | Trigger phrase in problem | Core identity / move | Verify by |
|---|---|---|---|---|
| 1 | Multiply/add stages | independent positions/choices | ∏ options; Σ disjoint cases | independence sentence + n=2 |
| 2 | Binomial / paths | grid paths, ±1 steps, "choose positions" | C(n, k) + parity/range guards | 2×2 grid by hand |
| 3 | Interleavings | fixed internal order, free merge | C(m+n, m); n!/∏(subtree sizes) | 3-node tree |
| 4 | Catalan | balanced, non-crossing, tree shapes | first-decision convolution; C(2n,n)/(n+1) | spot 1, 2, 5, 14 |
| 5 | Group-then-choose | count equal/similar pairs | hashmap key + C(k,2) or k(k−1) | ordered-vs-unordered check |
| 6 | Contribution flip | Σ over all subsequences/subarrays | element × multiplicity; powers of 2; span products | tiny array brute force |
| 7 | Inclusion–exclusion | "divisible by any of", unions | ± by subset parity; lcm intersections | count(x) vs brute list |
| 8 | Unranking | "k-th object, k huge" | skip blocks by computable size; k -= 1 first | k=1 and k=max cases |
| 9 | Digit-tree counting | "numbers ≤ N with ⟨digit property⟩" | tight-prefix walk; subtree sizes | small N by brute force |