Comparing Text with Longest Common Subsequence

Step 5: Comparing Text with Longest Common Subsequence

The longest common subsequence (LCS) is the length of the longest sequence of characters that appears in both strings in the same order, though not necessarily contiguously. It is the backbone of line-based diff tools and version control.

Implement longest_common_subsequence(a, b). Build a table dp where dp[i][j] is the LCS length of the first i characters of a and the first j characters of b.

If the current characters match, the LCS grows by one on top of dp[i-1][j-1]. If they do not match, the best you can do is carry the larger of dp[i-1][j] and dp[i][j-1]. The row and column for empty prefixes are all zero.

Examples:

  • longest_common_subsequence("abcde", "ace") returns 3
  • longest_common_subsequence("abc", "abc") returns 3
  • longest_common_subsequence("abc", "def") returns 0
Hints
Starter code
def climb_stairs(n):
    """Number of ways to climb n stairs taking 1 or 2 steps, using memoization."""
    memo = {}

    def ways(k):
        if k < 0:
            return 0
        if k == 0:
            return 1
        if k in memo:
            return memo[k]
        memo[k] = ways(k - 1) + ways(k - 2)
        return memo[k]

    return ways(n)


def coin_change(coins, amount):
    """Minimum number of coins to make amount, or -1 if impossible (tabulation)."""
    INF = amount + 1
    dp = [0] + [INF] * amount
    for a in range(1, amount + 1):
        for c in coins:
            if c <= a and dp[a - c] + 1 < dp[a]:
                dp[a] = dp[a - c] + 1
    return dp[amount] if dp[amount] != INF else -1


def knapsack(weights, values, capacity):
    """Max total value with total weight <= capacity (0/1 knapsack, tabulation)."""
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        w = weights[i - 1]
        v = values[i - 1]
        for cap in range(capacity + 1):
            dp[i][cap] = dp[i - 1][cap]
            if w <= cap and dp[i - 1][cap - w] + v > dp[i][cap]:
                dp[i][cap] = dp[i - 1][cap - w] + v
    return dp[n][capacity]


def edit_distance(a, b):
    """Levenshtein distance between a and b (insert/delete/replace cost 1)."""
    m = len(a)
    n = len(b)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(m + 1):
        dp[i][0] = i
    for j in range(n + 1):
        dp[0][j] = j
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if a[i - 1] == b[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
    return dp[m][n]


def longest_common_subsequence(a, b):
    """Length of the longest common subsequence of a and b (tabulation)."""
    # TODO: build a 2D dp table of size (len(a) + 1) by (len(b) + 1), all zeros.
    # For each i, j: if a[i - 1] == b[j - 1] then dp[i][j] = dp[i - 1][j - 1] + 1;
    # else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]).
    # Return dp[len(a)][len(b)].
    pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.