Skip to content
advancedPhase 6 · Dynamic Programming

2D DP

Master 2D state space problems like grid paths and edit distance.

1h 30m
6 problems
Topic Progress0%

Grid-Based DP Problems

Grid DP problems involve finding optimal paths or counts in a 2D grid.

Unique Paths

// Count paths from top-left to bottom-right (only right/down moves)
public int uniquePaths(int m, int n) {
    int[][] dp = new int[m][n];
    
    // Base cases: first row and first column have 1 path each
    for (int i = 0; i < m; i++) dp[i][0] = 1;
    for (int j = 0; j < n; j++) dp[0][j] = 1;
    
    // Fill the grid
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];  // From top + from left
        }
    }
    
    return dp[m - 1][n - 1];
}

// Space-optimized: O(n) space
public int uniquePathsOptimized(int m, int n) {
    int[] dp = new int[n];
    Arrays.fill(dp, 1);
    
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            dp[j] += dp[j - 1];  // dp[j] (from top) + dp[j-1] (from left)
        }
    }
    
    return dp[n - 1];
}

Minimum Path Sum

// Find path with minimum sum from top-left to bottom-right
public int minPathSum(int[][] grid) {
    int m = grid.length, n = grid[0].length;
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (i == 0 && j == 0) continue;  // Start cell
            else if (i == 0) grid[i][j] += grid[i][j - 1];  // First row
            else if (j == 0) grid[i][j] += grid[i - 1][j];  // First column
            else grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
        }
    }
    
    return grid[m - 1][n - 1];
}

Unique Paths with Obstacles

// Grid with obstacles (1 = obstacle, 0 = empty)
public int uniquePathsWithObstacles(int[][] grid) {
    int m = grid.length, n = grid[0].length;
    
    if (grid[0][0] == 1 || grid[m-1][n-1] == 1) return 0;
    
    int[][] dp = new int[m][n];
    dp[0][0] = 1;
    
    for (int i = 1; i < m; i++) {
        dp[i][0] = (grid[i][0] == 0 && dp[i-1][0] == 1) ? 1 : 0;
    }
    for (int j = 1; j < n; j++) {
        dp[0][j] = (grid[0][j] == 0 && dp[0][j-1] == 1) ? 1 : 0;
    }
    
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            if (grid[i][j] == 0) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
    }
    
    return dp[m-1][n-1];
}

Key Pattern for Grid DP

// State: dp[i][j] = optimal value at cell (i,j)
// Transition: dp[i][j] = f(dp[i-1][j], dp[i][j-1])  // for 4-directional
// Or: dp[i][j] = f(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])  // for diagonal

Space Optimization for Grid DP

// If transition only depends on previous row, use single row
int[] dp = new int[n];
for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
        if (i == 0 && j == 0) dp[j] = grid[0][0];
        else if (i == 0) dp[j] = dp[j-1] + grid[0][j];
        else if (j == 0) dp[j] = dp[j] + grid[i][0];
        else dp[j] = Math.min(dp[j], dp[j-1]) + grid[i][j];
    }
}
return dp[n-1];

String DP Problems

String DP problems involve comparing or transforming two strings.

Longest Common Subsequence (LCS)

// Find length of longest common subsequence of two strings
public int longestCommonSubsequence(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    int[][] dp = new int[m + 1][n + 1];
    
    // dp[i][j] = LCS length of text1[0..i-1] and text2[0..j-1]
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + 1;  // Characters match
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);  // Skip one
            }
        }
    }
    
    return dp[m][n];
}

// Space-optimized: O(n) space
public int lcsOptimized(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    int[] dp = new int[n + 1];
    
    for (int i = 1; i <= m; i++) {
        int prev = 0;  // dp[i-1][j-1]
        for (int j = 1; j <= n; j++) {
            int temp = dp[j];
            if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                dp[j] = prev + 1;
            } else {
                dp[j] = Math.max(dp[j], dp[j - 1]);
            }
            prev = temp;
        }
    }
    
    return dp[n];
}

Edit Distance

// Minimum operations to convert word1 to word2
// Operations: insert, delete, replace
public int editDistance(String word1, String word2) {
    int m = word1.length(), n = word2.length();
    int[][] dp = new int[m + 1][n + 1];
    
    // Base cases: converting empty string
    for (int i = 0; i <= m; i++) dp[i][0] = i;  // Delete all
    for (int j = 0; j <= n; j++) dp[0][j] = j;  // Insert all
    
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1];  // No operation needed
            } else {
                dp[i][j] = 1 + Math.min(
                    dp[i - 1][j - 1],  // Replace
                    Math.min(dp[i - 1][j],   // Delete
                             dp[i][j - 1])    // Insert
                );
            }
        }
    }
    
    return dp[m][n];
}

Longest Palindromic Subsequence

// Find longest palindromic subsequence in a string
public int longestPalindromeSubseq(String s) {
    int n = s.length();
    int[][] dp = new int[n][n];
    
    // Base case: single character
    for (int i = 0; i < n; i++) dp[i][i] = 1;
    
    // Fill for lengths 2 to n
    for (int len = 2; len <= n; len++) {
        for (int i = 0; i <= n - len; i++) {
            int j = i + len - 1;
            if (s.charAt(i) == s.charAt(j)) {
                dp[i][j] = dp[i + 1][j - 1] + 2;
            } else {
                dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
            }
        }
    }
    
    return dp[0][n - 1];
}

String DP Patterns

Problem State Transition
LCS dp[i][j] = LCS of s1[0..i-1], s2[0..j-1] match: dp[i-1][j-1]+1, else: max(dp[i-1][j], dp[i][j-1])
Edit Distance dp[i][j] = ops to convert s1[0..i-1] to s2[0..j-1] match: dp[i-1][j-1], else: 1+min(replace, delete, insert)
Palindrome dp[i][j] = LPS in s[i..j] match: dp[i+1][j-1]+2, else: max(dp[i+1][j], dp[i][j-1])

Practice Problems

0/1solved
Longest Common Subsequence
2D String DP

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0. A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.

Example:

Input: text1 = "abcde", text2 = "ace"

Output: 3

The longest common subsequence is "ace" and its length is 3.

Optimal Solution — O(m * n) time, O(m * n), can be optimized to O(min(m, n)) space

2D DP where dp[i][j] is LCS length of text1[0..i-1] and text2[0..j-1]. If characters match, extend LCS; otherwise, take max of skipping one character from either string.

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] dp = new int[m + 1][n + 1];
        
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        
        return dp[m][n];
    }
}

Edge Cases:

  • One or both strings are empty
  • Strings are identical
  • No common characters
  • One string is subsequence of the other

Quiz

1. In Edit Distance, what are the three operations allowed?

Question 1 options

2. What is the state transition for LCS when characters match?

Question 2 options

3. What is the primary purpose of 2D Dynamic Programming?

Question 3 options

4. What is a common mistake when implementing 2D Dynamic Programming?

Question 4 options

Flashcards

Question

What is the state transition for Edit Distance?

Answer

If chars match: dp[i][j] = dp[i-1][j-1]. Else: dp[i][j] = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) representing replace, delete, insert.

Question

How do you optimize 2D DP space from O(m*n) to O(n)?

Answer

When dp[i][j] only depends on current and previous row, use a single 1D array and update carefully left-to-right or right-to-left.

Question

What is 2D Dynamic Programming?

Answer

2D Dynamic Programming is a key concept in software engineering.

Question

When to use 2D Dynamic Programming?

Answer

Use 2D Dynamic Programming when building production systems that require reliability, scalability, and maintainability.

Question

2D Dynamic Programming best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1.Grid DP: dp[i][j] depends on neighbors (top, left, diagonal)
  • 2.String DP: dp[i][j] depends on comparing characters at positions i-1, j-1
  • 3.Space optimization uses single row when only previous row needed
  • 4.Always handle base cases for first row and first column

Interview Tips

  • Draw out small examples to understand state transitions
  • Clarify if subsequence (non-contiguous) or substring (contiguous)
  • Discuss space optimization after giving O(mn) solution
  • For Edit Distance, explain the three operations clearly

Cheat Sheet

2D DP Cheat Sheet

Grid DP Patterns:

  • Unique paths: dp[i][j] = dp[i-1][j] + dp[i][j-1]
  • Min path sum: dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
  • Obstacles: set dp[i][j] = 0 when obstacle

String DP Patterns:

  • LCS: match: dp[i-1][j-1]+1, else: max(dp[i-1][j], dp[i][j-1])
  • Edit Distance: match: dp[i-1][j-1], else: 1+min(replace, delete, insert)
  • Palindrome: match: dp[i+1][j-1]+2, else: max(dp[i+1][j], dp[i][j-1])

Space Optimization:

  • Use single row when transition depends on previous row only
  • Update direction matters (left-to-right vs right-to-left)

Base Cases:

  • First row/column usually initialized to 1 or 0
  • Empty string comparisons: dp[0][j] = j, dp[i][0] = i