Dynamic programming (DP) sounds scary but is one idea: do not solve the same subproblem twice. When a recursive solution keeps recomputing identical calls, you cache the results — and an exponential algorithm collapses to polynomial.
DP applies when a problem has two properties: overlapping subproblems (the same smaller problems recur) and optimal substructure (the best answer is built from best answers to subproblems). Fibonacci is the canonical teaching example.
The problem: exponential recomputation
Naive fib(n) = fib(n-1) + fib(n-2) re-derives the same values over and over. The call tree branches exponentially — fib(5) already computes fib(2) three times.
Fix 1 — Memoization (top-down)
Keep the natural recursion but store each result the first time you compute it. Every subproblem is then solved once: O(2ⁿ) → O(n).
function fib(n, memo = new Map()) {
if (n < 2) return n
if (memo.has(n)) return memo.get(n) // reuse
const result = fib(n - 1, memo) + fib(n - 2, memo)
memo.set(n, result) // remember
return result
}
Fix 2 — Tabulation (bottom-up)
Flip it around: fill a table from the smallest subproblems up to the answer, so every value you need is already computed. No recursion, no stack — often the interviewer’s preferred form.
| ∅ | B | D | C | A | B | |
|---|---|---|---|---|---|---|
| ∅ | 0 | 0 | 0 | 0 | 0 | 0 |
| A | 0 | 0 | 0 | 0 | 1 | 1 |
| B | 0 | 1 | 1 | 1 | 1 | 2 |
| C | 0 | 1 | 1 | 2 | 2 | 2 |
| B | 0 | 1 | 1 | 2 | 2 | 3 |
| D | 0 | 1 | 2 | 2 | 2 | 3 |
| A | 0 | 1 | 2 | 2 | 3 | 3 |
| B | 0 | 1 | 2 | 2 | 3 | 4 |
function fib(n) {
if (n < 2) return n
let a = 0, b = 1
for (let i = 2; i <= n; i++) [a, b] = [b, a + b]
return b
}
How to spot and solve a DP problem
- Signals: "count the ways", "min/max cost", "can you reach…", "longest/shortest subsequence".
- Define the state — what parameters uniquely describe a subproblem (e.g.
dp[i]= best answer using the first i items). - Write the recurrence — how a state is built from smaller states.
- Set the base cases, then compute top-down (memo) or bottom-up (table).
- Optionally reduce space by keeping only the rows/columns you still need.
| Operation | Time |
|---|
Complexity = number of distinct states × work per state. For 1-D fib that is O(n); for LCS it is O(m·n).
Interview reflex
Stuck on a DP? First write the brute-force recursion, confirm it has overlapping subproblems, then add a cache. Getting from correct-but-slow recursion to memoized DP is the move interviewers want to see.
Classic DP problems to know
Climbing stairs, coin change, house robber (1-D); knapsack, longest common subsequence, edit distance, unique paths (2-D). Master these patterns and most DP interview questions become variations.