Dynamic programming is the interview pattern for problems where brute force recomputes the same subproblems again and again. The hardest part is not coding loops. It is defining the right state and proving the transition.
Strong candidates do not jump straight to a table. They first explain what the subproblem means, why the recurrence is complete, and whether memoization or tabulation is the clearer implementation.
Interview lens
A strong DP explanation usually has four parts:
- what the state represents,
- what transition combines smaller answers into the current one,
- which base cases make the recurrence valid,
- why memoization or tabulation avoids repeated work.
Pattern Summary Table
| Pattern | When to use | Key idea | Example problem |
|---|---|---|---|
| 1D DP | answer at position i depends on a small number of earlier positions |
roll forward a small state window | House Robber |
| 2D DP | answer depends on two varying dimensions or prefixes | table cell represents a pair of subproblem boundaries | Longest Common Subsequence |
| Top-down memoization | recurrence is easier to write recursively | cache solved subproblems to avoid recomputation | Climbing Stairs |
Problem Statement
Given an optimization, counting, or decision problem with overlapping subproblems, compute the best answer without recomputing the same states exponentially many times.
Typical interview signals:
- brute force recursion branches heavily
- the same subproblem appears from multiple parent calls
- the answer is built from smaller prefix, suffix, or subset answers
- the prompt asks for max, min, count, or feasibility over many choices
Pattern Recognition Signals
- Keywords in the problem: maximum, minimum, count ways, choose or skip, subsequence, partition, recurrence.
- Structural signals: subproblems overlap and the final answer has optimal substructure.
- Complexity signal: naive recursion would be exponential, but distinct states are far fewer.
Visual Intuition
DP solves optimization/counting problems with overlapping subproblems. The hardest part is state definition, not coding loops.
DP Design Checklist
- Define state clearly.
- Write transition relation.
- Set base cases.
- Choose memoization or tabulation.
- Optimize space if needed.
Pattern 1: 1D DP (House Robber)
Problem description: Choose houses to maximize money without robbing adjacent houses.
What we are doing actually:
- At each house, decide between skipping it or taking it.
prev1stores the best answer up to the previous house.prev2stores the best answer up to the house before that.
public int rob(int[] nums) {
int prev2 = 0, prev1 = 0;
for (int x : nums) {
int cur = Math.max(prev1, prev2 + x); // Skip current house or rob it.
prev2 = prev1; // Shift states forward.
prev1 = cur;
}
return prev1;
}
Debug steps:
- print
x,prev2,prev1, andcureach iteration - verify the state shift order is correct
- test small arrays of size 1, 2, and 3
Dry Run (House Robber)
Input: [2,7,9,3,1]
State:
prev1= best up to previous houseprev2= best up to house before previous
Steps:
- x=2 -> cur=max(0,0+2)=2
- x=7 -> cur=max(2,0+7)=7
- x=9 -> cur=max(7,2+9)=11
- x=3 -> cur=max(11,7+3)=11
- x=1 -> cur=max(11,11+1)=12
Answer: 12
Pattern 2: 2D DP (LCS)
Problem description: Find the length of the longest common subsequence between two strings.
What we are doing actually:
dp[i][j]represents the answer for prefixesa[0..i-1]andb[0..j-1].- If characters match, extend the diagonal answer.
- Otherwise take the better answer from top or left.
public int longestCommonSubsequence(String a, String b) {
int m = a.length(), n = b.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (a.charAt(i - 1) == b.charAt(j - 1)) {
dp[i][j] = 1 + dp[i - 1][j - 1]; // Matching character extends subsequence.
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); // Skip one char from either string.
}
}
}
return dp[m][n];
}
Debug steps:
- print the DP table for a tiny pair like
"abc"and"ac" - verify row
0and column0stay at zero - inspect one match cell and one mismatch cell manually
Pattern 3: Top-Down Memoization (Climbing Stairs)
Problem description:
Count how many distinct ways there are to climb n stairs when you can take 1 or 2 steps.
What we are doing actually:
- Express the recurrence recursively.
- Cache computed answers in
memo. - Reuse cached values instead of recomputing the same subproblems.
public int climbStairs(int n) {
int[] memo = new int[n + 1];
Arrays.fill(memo, -1);
return solve(n, memo);
}
private int solve(int n, int[] memo) {
if (n <= 2) return n;
if (memo[n] != -1) return memo[n]; // Reuse cached result.
memo[n] = solve(n - 1, memo) + solve(n - 2, memo); // Recurrence relation.
return memo[n];
}
Debug steps:
- print
nand whether the value came from memo or fresh computation - verify base cases before memo lookup
- compare recursion count with and without memoization on a small example
Common Mistakes
- Wrong or incomplete state definition
- Transition using uncomputed states
- Incorrect base cases
- Exponential recursion without memoization
Top-Down vs Bottom-Up Heuristic
- use top-down when recurrence is easier to express recursively
- use bottom-up when dependency order is clear and iterative performance matters
For interview speed, start with top-down to prove recurrence, then convert to tabulation if needed.
DP Debug Checklist
- print first few DP states/tables
- verify base row/column initialization
- validate transition with a tiny handcrafted example
- ensure answer cell/index matches problem definition
DP bugs are often indexing/base-case bugs, not formula bugs.
Pattern Variations
- knapsack-style DP
- subsequence and string DP
- interval DP
- DP on trees, digits, and bitmasks in advanced follow-ups
Pattern Composition (Advanced)
- DP + prefix sums for faster transitions
- DP + monotonic deque or convex-style optimization for range-limited transitions
- DP + graphs or topological order when states form a DAG
Practice Problems
- Climbing Stairs (LC 70)
LeetCode - House Robber (LC 198)
LeetCode - Coin Change (LC 322)
LeetCode - Longest Increasing Subsequence (LC 300)
LeetCode - Longest Common Subsequence (LC 1143)
LeetCode - Edit Distance (LC 72)
LeetCode
Key Takeaways
- the state definition is the real design decision
- memoization proves the recurrence quickly, while tabulation often gives better iterative control
- most DP bugs are state-definition, base-case, or indexing mistakes
- optimize space only after the full state transition is already correct
Categories
Tags
Comments