Pattern #23
Two-Dimensional DP
EssentialState depends on two changing indices (grid, two strings, two pointers in DP).
When to use
Paths in a grid, edit distance, LCS, or any recurrence with two parameters i and j.
Recognition cues
- Unique paths / min path sum
- Edit distance / LCS
- dp[i][j] from neighbors
Common pitfalls
- Wrong base row/column initialization
- Iterating in an order that reads unfilled cells
- Confusing inclusive lengths vs indices
90-second recognition drill
Which pattern fits best?
- Unique paths / min path sum
- Edit distance / LCS
- dp[i][j] from neighbors
Interactive
Mental model
A full worked walkthrough of the invariant. Pause, scrub the dots, or use ← →. Aim to narrate each step yourself.
Step 1 of 8
1
1
1
1
?
?
1
?
?
dp[r][c]=up+left
Unique paths on a 3x3 grid. Borders are 1.
How to think about it
dp[i][j] = answer for a subproblem on prefix i and j (or cell (i,j)). Fill by increasing i+j or scanning rows. Often the answer sits at dp[m][n] or dp[m-1][n-1].
Template shapes
| Shape | Core move | Notes |
|---|---|---|
| Grid paths | from top/left | Obstacles zero out |
| String DP | match → diag+1 | else min/max of skips |
| Rolling array | Keep prev row only | Save space |
Complexity baseline
O(m·n) time and space (or O(min(m,n)) space with rolling).
From template to problem
- Define dp[i][j] in words.
- Initialize first row/col.
- Transition from the geometric neighbors of the recurrence.
- Return the corner cell.
Template
Same skeleton in TypeScript, Python, and C#. Adapt the invariant; keep the structure.
Two-Dimensional DP · Template
/** 2D DP template: unique paths (right/down only). dp[c] rolled per row. */
export function uniquePaths(m: number, n: number): number {
const dp = new Array<number>(n).fill(1);
for (let r = 1; r < m; r++) {
for (let c = 1; c < n; c++) dp[c] = dp[c]! + dp[c - 1]!;
}
return dp[n - 1]!;
}
/** 2D DP template: unique paths (right/down only). dp[c] rolled per row. */
export function uniquePaths(m: number, n: number): number {
const dp = new Array<number>(n).fill(1);
for (let r = 1; r < m; r++) {
for (let c = 1; c < n; c++) dp[c] = dp[c]! + dp[c - 1]!;
}
return dp[n - 1]!;
}
#StatusProblemTypeDifficultyDone
- 1#62 Unique PathsGuidemedium
- 2#63 Unique Paths IIGuidemedium
- 3#64 Minimum Path SumGuidemedium
- 4#72 Edit DistanceGuidemedium
- 5#221 Maximal SquareGuidemedium
- 6#1143 Longest Common SubsequenceGuidemedium