Overview
2D dynamic programming uses a state with two parameters, stored in a table dp[i][j]. The two dimensions usually mean positions in two sequences (compare strings), a position and a capacity (knapsack), a range [i, j] (interval DP), or a grid cell (paths).
The method is the same as 1D DP, but you must decide the fill order so that every cell's dependencies are computed first: row by row for sequence problems, by increasing length for interval problems. Many 2D tables can be compressed to one row because each row depends only on the previous one.
A road atlas table lists distances between pairs of cities. Each entry can be derived from smaller entries (going through an intermediate city). Filling the table in the right order means every entry you need is already written when you get to it.
When to use it
- Two strings or arrays must be compared or aligned.
- A choice depends on a position and a remaining budget (capacity, k operations).
- The answer for a range [i, j] depends on smaller ranges (palindromes, burst balloons).
- Paths or costs over a grid.
Problem patterns it solves
Recognize it when: compare prefixes of two strings: match, insert, delete, replace.
- 1143. Longest Common Subsequence
- 72. Edit Distance
- 583. Delete Operation for Two Strings
- 97. Interleaving String
Recognize it when: answer for s[i..j] built from shorter ranges.
- 516. Longest Palindromic Subsequence
- 5. Longest Palindromic Substring
- 312. Burst Balloons
- 1039. Minimum Score Triangulation of Polygon
Recognize it when: items processed so far and remaining capacity / operations.
- 416. Partition Equal Subset Sum
- 474. Ones and Zeroes
- 1235. Maximum Profit in Job Scheduling
Recognize it when: wildcards or regex against a string.
- 10. Regular Expression Matching
- 44. Wildcard Matching
- 115. Distinct Subsequences
Recognize it when: count or optimize paths moving right / down.
- 62. Unique Paths
- 64. Minimum Path Sum
- 221. Maximal Square
Where it is used in real software
git diff and code review tools compute a longest common subsequence of lines between two file versions.
Edit distance (Levenshtein) ranks dictionary words by how many edits separate them from a typo.
Needleman-Wunsch and Smith-Waterman fill a 2D table to align genes and proteins.
Dynamic time warping aligns two time series of different speeds using a 2D DP table.
Key terms
- dp[i][j]
- Answer for the first i items of one input and the first j of another (or range i..j).
- Fill order
- The sequence of cells ensuring dependencies are ready.
- Interval DP
- States are ranges, filled by increasing length.
- Row compression
- Keep only the previous row (or one row updated carefully) to use O(n) space.
Edit distance, step by step
- 1State
dp[i][j] = minimum edits to convert word1[0..i) into word2[0..j).
- 2Base cases
dp[i][0] = i (delete everything); dp[0][j] = j (insert everything).
- 3Characters match
If word1[i - 1] === word2[j - 1], dp[i][j] = dp[i - 1][j - 1] (no edit needed).
- 4Characters differ
1 + min(dp[i - 1][j] delete, dp[i][j - 1] insert, dp[i - 1][j - 1] replace).
- 5Answer
dp[m][n].
Edit distance from "horse" to "ros"
Rows: prefixes of horse; columns: "", r, o, s
| "" | r | o | s | |
|---|---|---|---|---|
| "" | 0 | 1 | 2 | 3 |
| h | 1 | 1 | 2 | 3 |
| o | 2 | 2 | 1 | 2 |
| r | 3 | 2 | 2 | 2 |
| s | 4 | 3 | 3 | 2 |
| e | 5 | 4 | 4 | 3 |
NOW: "" | "": 0 | r: 1 | o: 2 | s: 3
dp[5][3] = 3: replace h with r (rorse), delete r (rose), delete e (ros). Each cell looked only at its top, left, and top-left neighbors.
Implementation
function minDistance(word1, word2) { const m = word1.length, n = word2.length; const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0)); for (let i = 0; i <= m; i++) dp[i][0] = i; for (let j = 0; j <= n; j++) dp[0][j] = j; for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (word1[i - 1] === word2[j - 1]) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]); } } return dp[m][n];} // Interval DP: 516. Longest Palindromic Subsequence, filled by increasing lengthfunction longestPalindromeSubseq(s) { const n = s.length; const dp = Array.from({ length: n }, () => new Array(n).fill(0)); for (let i = n - 1; i >= 0; i--) { dp[i][i] = 1; for (let j = i + 1; j < n; j++) { dp[i][j] = s[i] === s[j] ? dp[i + 1][j - 1] + 2 : Math.max(dp[i + 1][j], dp[i][j - 1]); } } return dp[0][n - 1];} // 5. Longest Palindromic Substring by expanding around centers (O(n^2), O(1) space)function longestPalindrome(s) { let start = 0, best = 0; const expand = (l, r) => { while (l >= 0 && r < s.length && s[l] === s[r]) { l--; r++; } if (r - l - 1 > best) { best = r - l - 1; start = l + 1; } }; for (let i = 0; i < s.length; i++) { expand(i, i); expand(i, i + 1); } return s.slice(start, start + best);}Complexity and performance
One cell per pair of prefixes.
O(n^3) when splitting each range.
Often O(n) with row compression.
Trade-offs
The full table lets you reconstruct the actual edits or subsequence. Compressed rows save memory but only give the value.
Memoized recursion is natural for interval DP; bottom-up avoids recursion depth on long strings.
Variants and related techniques
Walk back from dp[m][n], following the choice that produced each value, to output the actual alignment.
Give insert, delete, and replace different costs.
For long strings over small alphabets, bit-parallel algorithms speed up LCS by 64x.
Common mistakes
- Wrong fill order in interval DP.
Fix: Iterate i from n - 1 down to 0 (or by increasing length) so dp[i + 1][...] is ready.
- Overwriting values needed later in 1D compression.
Fix: Save dp[j - 1] from the previous row in a temporary variable or use two rows.
- Confusing subsequence with substring.
Fix: Subsequences skip characters (take max of neighbors); substrings must be contiguous (reset to 0 on mismatch).
Interview questions
How do you decide the dimensions of a DP table?
List what changes between subproblems. If two independent indexes (or an index and a budget) are needed to describe a subproblem uniquely, the state is 2D.
How do you reduce edit distance space to O(n)?
Each row depends only on the previous row and the current row's left cell, so keep two arrays and swap them.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 1143. Longest Common Subsequence | Medium | Two sequences. |
| 72. Edit Distance | Medium | Three transitions. |
| 516. Longest Palindromic Subsequence | Medium | Interval DP. |
| 97. Interleaving String | Medium | Two pointers as state. |
| 10. Regular Expression Matching | Hard | Pattern DP. |
| 312. Burst Balloons | Hard | Last choice in a range. |