Courses › Coding Interviews › Patterns
Dynamic Programming
From recursion to memoization to tabulation — with the common DP shapes.
Dynamic programming (DP) is recursion plus memory. When a problem breaks into overlapping subproblems and the best answer is built from the best answers to smaller pieces (optimal substructure), you solve each subproblem once and reuse it. DP has a reputation for difficulty, but nearly every interview DP problem follows a handful of shapes.
When to use it
- "Minimum / maximum cost / ways / length…"
- "How many ways to…"
- "Is it possible to…" with choices at each step
- A brute-force recursion that recomputes the same arguments over and over
The four-step method
- Define the state. What does
dp[i](ordp[i][j]) mean, in words? This is the crucial step. - Write the recurrence. How does a state depend on smaller states?
- Set base cases.
- Choose the order — top-down with memoization, or bottom-up filling a table.
Top-down vs bottom-up
from functools import cache
def climb_stairs(n):
@cache
def ways(i):
if i <= 1:
return 1
return ways(i - 1) + ways(i - 2)
return ways(n)def climb_stairs(n):
prev, cur = 1, 1
for _ in range(n - 1):
prev, cur = cur, prev + cur
return curTop-down is often easier to derive in an interview — start there, then convert if asked about space.
Common shapes
Worked example: coin change
State: dp[a] = fewest coins that sum to a. Recurrence: dp[a] = 1 + min(dp[a - c] for c in coins if c <= a). Base: dp[0] = 0.
def coin_change(coins, amount):
INF = float("inf")
dp = [0] + [INF] * amount
for a in range(1, amount + 1):
for c in coins:
if c <= a and dp[a - c] + 1 < dp[a]:
dp[a] = dp[a - c] + 1
return dp[amount] if dp[amount] != INF else -1Worked example: edit distance (two sequences)
State: dp[i][j] = edits to turn a[:i] into b[:j].
def edit_distance(a, b):
m, n = len(a), len(b)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1): dp[i][0] = i
for j in range(n + 1): dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if a[i - 1] == b[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], # delete
dp[i][j - 1], # insert
dp[i - 1][j - 1]) # replace
return dp[m][n][!INTERVIEW] Narrate the state definition Say the state in plain English before writing code: "dp of i is the fewest coins needed to make amount i." Interviewers grade the reasoning, and a clear state definition makes the recurrence almost write itself.
Pitfalls
- A vague state definition — the most common reason DP solutions go wrong
- Wrong loop order for 0/1 vs unbounded knapsack (iterate capacity backward for 0/1 when using a 1D array)
- Forgetting the "impossible" case (infinity / −1)