Dynamic Programming on Strings: LCS, Edit Distance, and Patterns (2025)

Dynamic Programming on Strings

String DP problems are among the most common in technical interviews, especially at Meta, Amazon, and Google. The unifying insight: when comparing two strings, define dp[i][j] as the answer for some prefix of s1 (length i) and some prefix of s2 (length j). The recurrence almost always branches on whether the current characters match.

The Two-String DP Template

# For problems comparing two strings s1 (len m) and s2 (len n):
# dp[i][j] = answer for s1[:i] and s2[:j]
dp = [[0] * (n + 1) for _ in range(m + 1)]
# Base cases: dp[0][j] and dp[i][0] (empty string comparisons)
for i in range(1, m + 1):
    for j in range(1, n + 1):
        if s1[i-1] == s2[j-1]:
            dp[i][j] = ...   # use dp[i-1][j-1]
        else:
            dp[i][j] = ...   # use dp[i-1][j], dp[i][j-1], dp[i-1][j-1]

Pattern 1: Longest Common Subsequence (LC 1143)

def longest_common_subsequence(s1: str, s2: str) -> int:
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1   # characters match: extend LCS
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])  # skip one char from either
    return dp[m][n]

Time O(m*n), Space O(m*n) — reducible to O(n) with rolling array. LCS is the foundation for diff tools (git diff), DNA sequence alignment, and plagiarism detection.

Pattern 2: Edit Distance (LC 72)

Minimum operations (insert, delete, replace) to transform s1 into s2.

def min_distance(s1: str, s2: str) -> int:
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    # Base cases: transforming empty string
    for i in range(m + 1): dp[i][0] = i  # delete all chars of s1
    for j in range(n + 1): dp[0][j] = j  # insert all chars of s2
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1]          # no operation needed
            else:
                dp[i][j] = 1 + min(
                    dp[i-1][j],    # delete from s1
                    dp[i][j-1],    # insert into s1
                    dp[i-1][j-1]   # replace in s1
                )
    return dp[m][n]

Classic application: spell checkers (suggest words within edit distance 2), fuzzy search, bioinformatics.

Pattern 3: Longest Palindromic Subsequence (LC 516)

LCS of s and reverse(s). Or define dp[i][j] = LPS of s[i..j].

def longest_palindromic_subsequence(s: str) -> int:
    # Method 1: LCS with reverse
    return longest_common_subsequence(s, s[::-1])

def longest_palindromic_subsequence_v2(s: str) -> int:
    # Method 2: Interval DP
    n = len(s)
    dp = [[0] * n for _ in range(n)]
    for i in range(n): dp[i][i] = 1  # single char is palindrome of length 1
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            if s[i] == s[j]:
                dp[i][j] = dp[i+1][j-1] + 2
            else:
                dp[i][j] = max(dp[i+1][j], dp[i][j-1])
    return dp[0][n-1]

Pattern 4: Distinct Subsequences (LC 115)

Count distinct ways s contains t as a subsequence.

def num_distinct(s: str, t: str) -> int:
    m, n = len(s), len(t)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(m + 1): dp[i][0] = 1  # empty t is always a subsequence
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            dp[i][j] = dp[i-1][j]           # skip s[i-1]: ways without this char
            if s[i-1] == t[j-1]:
                dp[i][j] += dp[i-1][j-1]    # use s[i-1] to match t[j-1]
    return dp[m][n]

Pattern 5: Shortest Common Supersequence (LC 1092)

Shortest string containing both s1 and s2 as subsequences. Length = m + n – LCS(s1, s2).

def shortest_common_supersequence(s1: str, s2: str) -> str:
    # Build LCS dp table, then reconstruct supersequence
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    # Reconstruct: trace back dp table
    result = []
    i, j = m, n
    while i > 0 and j > 0:
        if s1[i-1] == s2[j-1]:
            result.append(s1[i-1]); i -= 1; j -= 1
        elif dp[i-1][j] > dp[i][j-1]:
            result.append(s1[i-1]); i -= 1
        else:
            result.append(s2[j-1]); j -= 1
    result.extend(reversed(s1[:i]))
    result.extend(reversed(s2[:j]))
    return ''.join(reversed(result))

Space Optimization

All two-string DP tables can be reduced from O(m*n) to O(n) space using a rolling array — only the previous row is needed:

def lcs_space_optimized(s1, s2):
    m, n = len(s1), len(s2)
    prev = [0] * (n + 1)
    for i in range(1, m + 1):
        curr = [0] * (n + 1)
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                curr[j] = prev[j-1] + 1
            else:
                curr[j] = max(prev[j], curr[j-1])
        prev = curr
    return prev[n]

Interview Tips

  • Always draw the dp table on a whiteboard with a small example — it makes the recurrence obvious.
  • State the recurrence in English before coding: “dp[i][j] is the LCS of the first i chars of s1 and first j chars of s2.”
  • Fill order matters: both i and j must increase left-to-right, top-to-bottom to use values from dp[i-1][…] and dp[…][j-1].
  • Edit Distance vs LCS: they look similar but serve different purposes. Edit Distance counts operations; LCS counts matching characters.

🏢 Asked at: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering

🏢 Asked at: Databricks Interview Guide 2026: Spark Internals, Delta Lake, and Lakehouse Architecture

🏢 Asked at: Apple Interview Guide 2026: iOS Systems, Hardware-Software Integration, and iCloud Architecture

🏢 Asked at: Atlassian Interview Guide

🏢 Asked at: Coinbase Interview Guide

Scroll to Top