LC Patterns — Math & Number Theory
The Master Insight (read before the patterns)
Math problems on LeetCode are anti-simulation problems: the naive answer is "loop and compute", and the intended answer is a piece of structure — a formula, an identity, a periodicity, an invariant — that collapses the loop. The interview is testing whether you look for structure before you reach for iteration.
n ≤ 10¹⁸, "return answer mod 10⁹+7"), digits/bases, "how many ways", divisibility/factors/primes, "without using ×/÷/pow", games with optimal play, or any statement that smells like a puzzle rather than a data-structure exercise. The phrase "mod 10⁹ + 7" is a flashing sign: the true answer is astronomically large ⇒ counting/combinatorics/fast-power, never enumeration.Three habits separate strong candidates on math problems:
- Try small cases by hand first (n = 1..6) and look for the pattern/period/formula — say you're doing it; it's a method, not flailing.
- State the identity before coding it ("digit sums are ≡ the number mod 9", "divisors pair up around √n") — the identity is the solution; the code is transcription.
- Announce overflow/precision handling even in Python ("Python ints are arbitrary precision; in Java I'd need long here, and I'd mod after every multiply") — a free seniority signal, and some problems (7, 8) make 32-bit limits part of the spec.
§0The Toolbox & Universal Pitfalls
Identities and routines to have at your fingertips
import math
math.gcd(a, b); math.lcm(a, b) # lcm(a,b) = a*b // gcd(a,b)
math.comb(n, k); math.isqrt(n) # exact integer nCr and √ — no float drift
pow(a, e, m) # fast modular exponentiation, O(log e)
pow(a, -1, m) # modular inverse (m prime or gcd(a,m)=1)
divmod(n, 10) # (n // 10, n % 10) — the digit-peeling idiom- Euclid, two lines:
while b: a, b = b, a % b— O(log min(a,b)). Bézout:ax + by = csolvable iffgcd(a,b) | c(this single fact is LC 365). - Sieve of Eratosthenes: O(n log log n); mark from
i*i, stepi, only for primei ≤ √n. - Divisors pair around √n: enumerate
i ≤ √n; each hit givesiandn // i. O(√n), and the perfect-square middle case appears once. - MOD discipline:
MOD = 10**9 + 7; reduce after every+and*. Division under mod does not exist — multiply by the modular inverse (Fermat:pow(a, MOD-2, MOD)since MOD is prime). Subtraction:(a - b) % MOD— fine in Python, needs+MODfirst in C++/Java.
Python-specific gotchas (these decide bugs, learn them cold)
//floors toward −∞, C/Java truncate toward 0.-7 // 2 == -4in Python,-7 / 2 == -3in Java. LC 7 / 29 expect truncation → useint(a / b)or work withabs+ sign. Same for%.- Floats lie.
0.1 + 0.2 != 0.3;math.sqrton big ints drifts. Usemath.isqrt, integer cross-multiplication (comparea*dvsb*c, nota/bvsc/d), andFractiononly if desperate. - Simulated 32-bit limits:
INT_MAX = 2**31 - 1. Problems 7/8/29 require you to detect overflow that Python won't produce — check bounds explicitly and say so.
Universal pitfalls
- Simulating what has a formula — the failure mode this whole file exists to prevent. If bounds exceed ~10⁸ operations, a formula/period/doubling is mandatory.
- Modding too late or unevenly — one missed
% MODin a product chain is an overflow (other languages) or a wrong-answer-on-huge-case. - Edge integers: 0, 1, negatives,
INT_MIN(whose absolute value overflows in C/Java — the classic 29 trap), n = 1 in "count/kth" problems. - Float equality anywhere — if you typed
==between floats, redesign with integers. - Not testing the formula — after deriving, verify against brute force for n ≤ 8 mentally. A formula off by one at n=2 fails 100% of cases.
1Digit Extraction & Reconstruction
What it is: Process a number digit-by-digit without converting to a string (or while pretending not to): reverse an integer, test a numeric palindrome, add one with carries. The atomic moves: peel with n % 10, shrink with n // 10, build with rev = rev * 10 + d.
Intuition: a decimal number is a stack of digits — % 10 pops, // 10 shrinks, *10 + d pushes onto a new number in reversed order. Reversal is therefore pop-everything-push-everything. Palindrome (9): reverse only half the number and compare (rev >= n stops the loop) — avoids any overflow question and handles odd lengths by rev // 10. Plus one (66): carries propagate only through trailing 9s.
Approach: loop while n: n, d = divmod(n, 10). For LC 7's overflow spec: check rev > (INT_MAX - d) // 10 before the push (predict overflow, don't detect it after). Negatives: strip the sign, restore at the end.
Challenges & pitfalls
Python's negative ///% silently corrupting digit peels (always work on abs); trailing zeros in palindromes (10 → false, but 0 → true); the half-reversal termination in 9 (rev >= n); resisting str(n)[::-1] — fine to mention, but the digit loop is what's examined.
x = 123 within 32-bit bounds.- Peel and rebuild:
123 → d=3, rev=3;12 → d=2, rev=32;1 → d=1, rev=321. Result 321. ✓ - Overflow case
x = 1534236469: reversing gives 9646324351, which exceedsINT_MAX = 2147483647. The pre-push check (rev > (INT_MAX − d)/10) catches this before the multiply → return 0 (per the 32-bit spec). The point of the exercise is predicting overflow, not detecting it after it happens.
2Manual Big-Number Arithmetic (schoolbook on strings)
What it is: Add or multiply numbers given as strings/arrays too large for fixed-width integers — reimplement the pencil-and-paper algorithms: right-to-left, per-position work, carry propagation.
Intuition: positional notation makes arithmetic local: position i of the result depends only on the input digits at/near i plus a carry. Addition: one zip from the right with a running carry. Multiplication (43): the key indexing fact — digit i × digit j lands in result positions i + j and i + j + 1 (from the right). Allocate len(a) + len(b) slots, accumulate raw products, then one carry-normalization pass.
Approach: addition template: two pointers from the ends, total = da + db + carry, emit total % base, carry = total // base, loop while i or j or carry, reverse at the end. Multiplication: res[i+j+1] += da*db double loop, then normalize carries right-to-left, strip leading zeros. Base 2 (67) is the same template with base = 2.
Challenges & pitfalls
forgetting the final carry (99 + 1); leading zeros after multiplication ("0" * "0" → "0", not ""); off-by-one in the i+j indexing (derive it on a 2×2 example); doing 43 via repeated addition (too slow); don't shortcut with int(s) in Python — legal syntax, instant fail on intent.
"456" + "77".- Add from the right with a carry:
6 + 7 = 13→ digit3, carry 1.5 + 7 + 1 = 13→ digit3, carry 1.4 + 0 + 1 = 5→ digit5, carry 0 (the shorter number contributes 0).- Collect digits and reverse → "533". ✓ The
while i or j or carryloop is what handles the shorter operand and a final leftover carry (as in"99" + "1").
3GCD / LCM & Bézout's Identity
What it is: Problems whose hidden skeleton is the greatest common divisor: a common chunk size (strings, groups, decks), reachable water amounts with two jugs, synchronizing periodic events. Euclid's algorithm plus two facts — lcm = a·b/gcd and Bézout — cover all of them.
Intuition: gcd is "the largest unit both quantities are made of." String version (1071): if s + t == t + s the two strings are repetitions of one common block, and the answer's length is gcd(len(s), len(t)). Deck of cards (914): a valid group size must divide every count → feasible iff gcd(all counts) ≥ 2. Water jugs (365): every reachable amount is a Bézout combination ax + by, so the target is reachable iff target ≤ x + y and gcd(x, y) | target.
Approach: math.gcd / functools.reduce(gcd, counts) for the fold. Prove the string case both directions if asked: commuting concatenation ⇒ common period.
Challenges & pitfalls
recognizing gcd at all (the tell in 365: two step-sizes and a reachability question); the t ≤ x + y capacity cap in 365 (Bézout ignores physical limits); reduce with an empty edge case; folding gcd short-circuits at 1 — mention the early exit.
str1 = "ABCABC", str2 = "ABC".- First check a common block even exists:
str1 + str2 == str2 + str1?"ABCABCABC" == "ABCABCABC"✓ → they're both repetitions of one block. - The block's length is
gcd(6, 3) = 3, so the largest common divisor string is the first 3 chars → "ABC". ✓ The concatenation-commutes test replaces a pile of case analysis.
4Primes: Sieve, Factorization, and Prime Counting
What it is: Anything needing many primality answers or prime factorizations: count primes below n, factor every element of an array, permutations constrained by primality. One precomputation — the sieve — converts per-query cost from O(√n) to O(1)-ish.
Intuition: primality of one number → trial division by candidates ≤ √n (any composite has a factor ≤ its square root). Primality of many numbers up to n → flip the direction: each prime announces its multiples — the sieve, O(n log log n). The upgrade: record for every number its smallest prime factor (SPF) during the sieve; then factoring any x is following SPF links — O(log x).
Approach: sieve template: is_prime = [True]*n, kill 0/1, for i in 2..isqrt(n): if prime, mark i*i, i*i+i, … False (starting at i*i because smaller multiples were killed by smaller primes). SPF variant: store spf[j] = i on first marking.
Challenges & pitfalls
starting marks at 2*i instead of i*i (correct but misses the optimization and its explanation); range(i*i, n, i) bounds off-by-one; factoring by trial division when SPF was the intent; for one-shot primality of a huge number, trial division to √n is the interview answer (Miller–Rabin is a name-drop).
n = 10.- Sieve: start all of 2..9 marked prime. Take 2 (prime) → cross out 4, 6, 8. Take 3 (prime) → cross out 9 (6 already gone).
4 > √10so stop marking. - Survivors: 2, 3, 5, 7 → 4 primes. ✓ Each prime announced its own multiples; starting the crossing-out at
i²(here4for i=2,9for i=3) avoids redoing work smaller primes already did.
5Divisor Enumeration (the √n mirror)
What it is: List, count, or sum the divisors of n — kth factor, perfect numbers, numbers with exactly four divisors. The single trick: divisors come in pairs (i, n/i) straddling √n, so full enumeration costs O(√n).
Intuition: if i | n then n/i | n, and one of the pair is ≤ √n — so scanning i = 1..⌊√n⌋ finds every divisor exactly once as the small half of its pair. Perfect square ⇒ i == n/i once — the self-paired middle. Formula view: if n = ∏ pᵢ^aᵢ, then #divisors = ∏(aᵢ+1) — knowing it lets you sanity-check (1390's "exactly four" means p·q or p³).
Approach: kth factor (1492): first pass over i ≤ √n collects small divisors counting toward k; second pass walks i back down, emitting large partners n//i in increasing order — skipping the middle when i*i == n. Perfect number (507): sum both halves of each pair, subtract n, compare.
Challenges & pitfalls
the perfect-square double-count (every problem here hides it); emitting large divisors in order for 1492; 1 and n inclusion (507 excludes n); loop bound i*i <= n vs i <= isqrt(n) (both fine; float sqrt is not); the complexity claim ("O(√n) per query — for many queries I'd precompute").
n = 12, k = 3.- Divisors of 12, in order: 1, 2, 3, 4, 6, 12. Enumerate small halves up to
√12 ≈ 3:1(partner 12),2(partner 6),3(partner 4). - Counting in increasing order, the 3rd factor is 3. ✓ You'd find the small divisors going up (1, 2, 3) and the large ones by walking their partners back down (4, 6, 12) — all in O(√n).
6Modular Arithmetic & Fast Exponentiation (binary doubling) ★
What it is: Compute x^n (or anything defined by repeated combination) for astronomically large n by squaring: n halves every step, O(log n). Under a modulus, this plus modular inverses is the engine behind every "count … mod 10⁹+7" problem.
Intuition: x^n = (x²)^(n/2), times one extra x when n is odd — each squaring consumes one binary digit of the exponent. Read n in binary: x^13 = x^8 · x^4 · x^1. The same doubling computes division without / (29), matrix powers (Fibonacci in O(log n)), and repeated composition. Super pow (372): process the exponent's digits, or reduce with Fermat (a^b ≡ a^(b mod (p−1))).
Approach: iterative template (recite-able):
def power(x, n, m):
res = 1
x %= m
while n:
if n & 1: res = res * x % m
x = x * x % m
n >>= 1
return resPython's built-in pow(x, n, m) does exactly this — use it, but be ready to write it. Division under mod = multiply by pow(a, p−2, p) (Fermat, p prime).
Challenges & pitfalls
the recursive version recomputing power(x, n//2) twice (O(n) — bind it or go iterative); forgetting % m on the squaring line; LC 29's INT_MIN / -1 overflow edge; explaining O(log n) ("the exponent loses a bit per iteration"); "anything associative can be doubled."
2^10.- Read 10 in binary:
1010= 8 + 2, so2^10 = 2^8 · 2^2. Build powers by repeated squaring:2^1 = 2,2^2 = 4,2^4 = 16,2^8 = 256. - Multiply the ones matching set bits:
256 · 4 =1024. ✓ Only ~log₂(10) ≈ 4 squarings, not 10 multiplications — the difference between O(log n) and O(n) that matters when n is 10¹⁸.
7Combinatorics: Count, Don't Enumerate
What it is: "How many ways/paths/arrangements…" where the answer is a closed form: binomial coefficients, factorials, Catalan numbers, or a small product formula — often mod 10⁹+7. The skill is mapping the story onto a standard counting object.
Intuition: unique paths (62): every path is a shuffle of (m−1) downs and (n−1) rights → C(m+n−2, m−1). Pascal (118/119): C(n,k) = C(n−1,k−1) + C(n−1,k), and row generation in O(k) via the ratio C(n,k+1) = C(n,k)·(n−k)/(k+1). Pickup/delivery (1359): the i-th pair has (2i−1)·i valid slots → ∏ i·(2i−1), discovered by trying n=1,2 by hand. Recognize on sight: C(n,k), Catalan C(2n,n)/(n+1), stars-and-bars, inclusion–exclusion.
Approach: small/one-shot → math.comb. Under a modulus → precompute factorials fact[i] and inverse factorials inv_fact[i] = pow(fact[i], p−2, p) once, then C(n,k) = fact[n]·inv_fact[k]·inv_fact[n−k] % p in O(1). Always state the bijection.
Challenges & pitfalls
dividing under mod without inverses (the #1 combinatorics bug); float math.factorial ratios drifting (use math.comb); off-by-one in the bijection (m+n−2 not m+n); recognizing Catalan dressed up; the confidence to answer "how many" without enumerating.
m = 3 by n = 7 grid, moving only right/down.- Any path is a sequence of
(m−1) = 2downs and(n−1) = 6rights, in some order — so counting paths = counting which of the2 + 6 = 8steps are the downs. - That's
C(8, 2) = 28. Answer 28. ✓ The bijection ("each path ↔ a choice of which steps go down") turns a grid DP into one binomial coefficient.
8Base Conversion & Positional Systems
What it is: Translate numbers between representations: decimal ↔ base-7, hex, Excel columns (base-26 without a zero), even base −2. The universal loop is divmod peeling; the interview content is in the irregular bases.
Intuition: standard base-b conversion is Pattern 1's digit peel with divmod(n, b). Excel columns (168/171) are the famous twist: digits run 1–26 with no zero — a bijective base-26. The fix is one line: subtract 1 before each divmod (n -= 1; n, r = divmod(n, 26)). Negative base (1017): same peel, but a negative remainder must be normalized up.
Approach: to-string: peel with divmod, append, reverse. From-string (171): Horner's rule left to right — n = n * 26 + val(c). For 168 recite "subtract one, then divide" and trace Z (26) and AA (27) by hand before submitting.
Challenges & pitfalls
treating Excel as ordinary base-26 (works until Z, then off-by-one forever); Python divmod with negative operands (verify on n=3 by hand); building strings by prepending (O(n²) — collect and reverse); n = 0 edge in any to-string conversion.
n = 28 to its column title.- It's base-26 with digits 1–26 and no zero, so subtract 1 before each division:
n = 28:28 − 1 = 27,divmod(27, 26) = (1, 1)→ digit 1 →'B'; carryn = 1.n = 1:1 − 1 = 0,divmod(0, 26) = (0, 0)→ digit 0 →'A';n = 0, stop.- Reverse the collected letters → "AB". ✓ Check:
AB = 26·1 + 2 = 28. The "subtract 1 each round" is what maps 1..26 onto 0..25 so ordinary division works — skip it andZ → AAbreaks.
9Sum Formulas & Algebraic Manipulation
What it is: The answer is an algebraic identity away: Gauss sums, telescoping, rearranging an equation until the loop disappears. Missing number via expected-sum, counting odds in a range via prefix subtraction, expressing n as sums of consecutive integers.
Intuition: Gauss: 1+2+…+n = n(n+1)/2 — missing number (268) is expected − actual, one pass. Range counting (1523): define f(x) = count of odds in [0,x] = (x+1)//2, answer f(hi) − f(lo−1) — prefix-function subtraction works for any monotone count. Consecutive-sum (829): n = k·a + k(k−1)/2 ⇒ n − k(k−1)/2 must be positive and divisible by k; loop k while k(k−1)/2 < n → O(√n).
Approach: write the constraint as an equation; isolate the unknown; read off the iteration space and divisibility conditions. Keep everything in integers.
Challenges & pitfalls
hashing/sorting what algebra solves in O(1) (offer the formula first); lo−1 underflow in prefix subtraction; deriving 829's bound live; sign/parity slips when rearranging (verify the formula on two hand cases).
nums = [3, 0, 1] contains all of 0..3 except one.- The full sum of 0..3 is
3·4/2 = 6(Gauss). The actual sum is3 + 0 + 1 = 4. - The missing number is
6 − 4 =2. ✓ One pass, no sorting or set — the formula collapses the search. (XOR of indices and values is an equally valid O(1)-space alternative worth offering.)
10Digit Processes, Fixed Points & Cycles
What it is: Repeatedly apply a digit-based map (sum of squared digits, sum of digits) until something stabilizes. Because the map crushes big numbers into a small range, the orbit must enter a cycle — detect it, or shortcut it with a known invariant.
Intuition: any n above a few hundred maps to something much smaller (sum of squared digits of a 13-digit number ≤ 13·81 = 1053), so all orbits fall into a bounded box and, by pigeonhole, eventually cycle. Happy number (202): a cycle means unhappy → detect with a seen-set or Floyd's tortoise-and-hare on the function. Digital root (258): the shortcut invariant — a number ≡ its digit sum mod 9 ⇒ answer is 1 + (n−1) % 9 for n > 0, O(1), no loop.
Approach: general template: iterate n = f(n) with a seen set until a fixed point or repeat. Upgrade 1: Floyd (O(1) space). Upgrade 2: a mathematical invariant that skips iteration (mod 9 for digit sums). Justify termination before the while-loop.
Challenges & pitfalls
proving the loop terminates (the pigeonhole sentence); the n=0/n=9 edges in the digital-root formula; explaining why mod 9 (10^k ≡ 1); choosing set vs Floyd (offer Floyd as the space upgrade, don't default to the fancy one).
19 happy (repeatedly sum the squares of digits, reach 1)?19 → 1² + 9² = 82 → 8² + 2² = 68 → 6² + 8² = 100 → 1² + 0² + 0² = 1. Reached 1 → happy (true). ✓- If it had entered a repeating cycle instead (which a seen-set or Floyd's cycle detection would catch), it'd be unhappy. The map always terminates because big numbers shrink into a bounded range, so by pigeonhole the orbit either hits 1 or loops.
38. Looping: 38 → 11 → 2. Or the O(1) invariant: 1 + (38 − 1) % 9 = 1 + 1 = 2. ✓11Inclusion–Exclusion + LCM Counting (with binary search on the answer)
What it is: "Find the n-th number divisible by a / b / c" or "n-th magical number" — the k-th element of a merged multiples sequence, where n is too big to enumerate. Combine a counting function built from inclusion–exclusion with binary search on the answer.
Intuition: you can't list the sequence, but you can count it: numbers ≤ x divisible by a or b = x//a + x//b − x//lcm(a,b) (subtract the double-counted; for three sets, add back the triple term, and lcm not product). count(x) is monotone in x ⇒ the n-th element is the smallest x with count(x) ≥ n — a boundary search. This composition — NT gives the predicate, binary search finds the boundary — is the pattern.
Approach: write count(x) from inclusion–exclusion (2 or 3 terms); binary search lo = 1, hi = n · min(divisors); return lo % (10**9+7) if asked. Sanity-check count on tiny x by hand against a brute list.
Challenges & pitfalls
using a*b where lcm is required (fails when inputs share factors); sign errors in the 3-set expansion; a too-small hi (silently wrong); the final mod applies to the found value; recognizing "n-th ⟨adjective⟩ number" with huge n.
n = 1, a = 2, b = 3 (a magical number is divisible by a or b); find the 1st.- Count of magical numbers ≤ x:
count(x) = x//2 + x//3 − x//lcm(2,3) = x//2 + x//3 − x//6. - Binary-search the smallest x with
count(x) ≥ 1: atx = 2,count = 1 + 0 − 0 = 1≥ 1, and atx = 1,count = 0. So the boundary is 2. - Answer 2. ✓ Using
lcm(2,3) = 6(not2·3) for the overlap term is the detail adversarial tests probe.
12Game Theory: Parity, Invariants & Backward Induction
What it is: Two players alternate moves, both playing optimally — who wins? The LC versions almost always collapse to a parity/invariant one-liner, and the interview is about finding it (via small cases) and proving it (via strategy or induction).
Intuition: work backward from losing positions. Nim (292): positions 1–3 win (take all), 4 loses (every move hands opponent 1–3), pattern locks in → n % 4 != 0; the proof is a strategy (whatever k the opponent takes, you take 4−k). Divisor game (1025): losing exactly when n is odd; parity is the invariant → n % 2 == 0. Stone game (877): with an even number of piles and unequal total, first player can commit to all-even or all-odd indexed piles → always true.
Approach: the method is the deliverable: (1) hand-compute win/lose for n = 1..8; (2) conjecture the pattern; (3) prove by exhibiting a strategy that preserves an invariant. If no clean pattern → it's a DP/minimax game, not a formula game.
Challenges & pitfalls
answering the formula without the strategy (interviewer asks "why?"); confusing "optimal" with "greedy" in small cases; 877's trap (coding interval DP when a two-line argument suffices); saying the method out loud so silence doesn't read as stuck.
n = 4?- Tabulate: n=1,2,3 are wins (take them all). n=4: whatever you take (1, 2, or 3) leaves 3, 2, or 1 — all wins for your opponent → 4 is a loss.
- The pattern: you lose exactly when
nis a multiple of 4 → answer for n=4 is false. ✓ The proof is a strategy: keep the count a multiple of 4 for your opponent by taking4 − (their move)each turn. (n=7 →7 % 4 ≠ 0→ true.)
13Geometry Essentials (integer-safe)
What it is: The small set of geometry problems that actually appear: points on a line, rectangle overlap/union area, distances. The unifying rule: stay in integers — cross-multiplied slopes, squared distances, interval intersections — and float bugs become unrepresentable.
Intuition: max points on a line (149): anchor each point; every other point defines a direction — same line ⟺ same reduced slope. Represent slope as a canonical fraction (dy//g, dx//g) with g = gcd and a fixed sign — hashable, exact, no division. Rectangles (223/836): work per-axis — two boxes overlap iff they overlap on x and on y; 1-D overlap length is max(0, min(r1, r2) − max(l1, l2)); union area = A + B − overlap.
Approach: 149: double loop with a per-anchor hashmap of direction → count; normalize (dy, dx) by gcd and force a canonical sign. Rectangles: dx = max(0, …), dy = max(0, …); overlap strictness (836 wants positive area — > not ≥). Distances: compare squared distances.
Challenges & pitfalls
vertical lines nuking float slope code (dx = 0 — integer pairs sidestep it); sign normalization so (1,−2) and (−1,2) hash together; 32-bit overflow in dx·dy; strict-vs-touching overlap (read the definition); convex hull / line-sweep are rare — deprioritize.
rec1 = [0,0,2,2] and rec2 = [1,1,3,3] overlap with positive area?- Check each axis separately. X: overlap length =
max(0, min(2,3) − max(0,1)) = max(0, 2 − 1) = 1 > 0. Y:max(0, min(2,3) − max(0,1)) = 1 > 0. - Both axes overlap positively → true. ✓ De-dimensioning the 2-D question into two 1-D interval checks, all in integers, avoids any floating-point comparison.
14Probability & Sampling (weighted pick, rejection, reservoir)
What it is: Design-flavored randomness: pick an index proportionally to weights, build a fair rand10 from rand7, sample uniformly from a stream of unknown length. Each has one named technique, and each demands you prove uniformity, not just produce randomness. (Full treatment in probability.)
Intuition: weighted pick (528): lay the weights end-to-end as segments on a number line — prefix sums; a uniform dart r ∈ [1, total] lands in exactly one segment; find it with binary search — probability ∝ segment length by construction. rand10-from-rand7 (470): rejection sampling — two rand7 calls give a uniform 1..49; accept ≤ 40 and take % 10 + 1, else retry. Reservoir sampling (382): keep the i-th element with probability 1/i; P(item i survives) telescopes to 1/n.
Approach: 528: precompute the prefix array; per query bisect_left(prefix, randint(1, total)). 470: while-loop rejection; never "fix up" rejected values. 382: single pass, random.randint(1, i) == 1 → replace.
Challenges & pitfalls
proving uniformity is the actual interview (rehearse all three 2-sentence proofs); off-by-one between bisect_left/bisect_right; using rejected samples in 470 (bias — explain why); the reservoir trigger phrase ("unknown length / single pass / O(1) memory"); the expected-cost analysis for rejection.
[1, 3]; index 0 should come up 1/4 of the time, index 1 three-quarters.- Prefix sums
[1, 4]cut a number line1..4into segments:[1]for index 0,[2, 3, 4]for index 1. - Throw a uniform dart
r ∈ [1, 4]and binary-search the first prefix ≥ r:r = 1 → index 0;r = 2, 3, 4 → index 1. - The segment lengths are 1 and 3, so the probabilities are exactly 1/4 and 3/4. ✓ The uniformity proof is the construction — "probability ∝ segment length."
▤Summary Table — Recognition Cheat Sheet
| # | Pattern | Trigger phrase in problem | Core tool | One-line key fact |
|---|---|---|---|---|
| 1 | Digit peel | "reverse integer", "palindrome number" | divmod(n, 10) | build reversed: rev*10 + d; check overflow before push |
| 2 | Big-number strings | "given as strings", "without int conversion" | schoolbook + carry | digit i × digit j → positions i+j, i+j+1 |
| 3 | GCD / Bézout | common chunk/group size, two jugs | Euclid, reduce(gcd) | ax+by=c solvable iff gcd(a,b) divides c |
| 4 | Primes / sieve | "count primes", factor many numbers | sieve (+ SPF) | mark from i²; SPF gives O(log x) factorization |
| 5 | Divisors | "kth factor", "perfect number" | √n pairing | divisors pair (i, n/i); square = self-pair |
| 6 | Fast power / mod | huge exponent, "mod 1e9+7", no / allowed | binary doubling | works for anything associative; O(log n) |
| 7 | Combinatorics | "how many ways", grid paths | nCr, fact+inv_fact | division under mod = multiply by inverse |
| 8 | Base conversion | Excel columns, base k, base −2 | divmod peel | bijective base-26: subtract 1 each round |
| 9 | Sum formulas | "missing", ranges, consecutive sums | Gauss, prefix f(hi)−f(lo−1) | algebra first; the loop bound falls out |
| 10 | Digit cycles | "repeat until", happy number | seen-set / Floyd / mod 9 | bounded orbit ⇒ pigeonhole ⇒ cycle |
| 11 | Incl–excl counting | "n-th number divisible by…" | count(x) + BS on answer | use lcm, not product; ± alternation |
| 12 | Game parity | two players, optimal play | small cases → invariant | prove with a strategy, not vibes |
| 13 | Geometry | points/lines/rectangles | integer slopes, per-axis | slope = reduced (dy, dx) pair; never float == |
| 14 | Sampling | random pick, stream sampling | prefix+BS, rejection, reservoir | prove uniformity — that's the question |