Lukas' Notes

Definition

Dynamic Programming Algorithm (Longest Common Subsequence)

Given two strings and , the dynamic programming algorithm for the longest common subsequence problem computes the optimum for pairs of prefixes and reuses those results.

Use zero-based indices. For a string , is the letter at index , and is the substring from index to index , including both endpoints.

Let be the length of an LCS of the prefixes and .

If one prefix is empty, no non-empty common subsequence is possible:

For non-empty prefixes, compare their last letters:

Return . Memoisation computes each required state at most once.

Pseudocode

Initialise all memo entries as unknown, then call lcs_rec(|S|-1, |T|-1).

int lcs_rec(int i, int j)
    if i < 0 or j < 0:
        return 0
    if memo[i][j] is known:
        return memo[i][j]
    if S[i] == T[j]:
        memo[i][j] := lcs_rec(i-1, j-1) + 1
    else:
        memo[i][j] := max(lcs_rec(i, j-1), lcs_rec(i-1, j))
    return memo[i][j]

Correctness

Prefix Recurrence

The recurrence computes the exact LCS length for every pair of prefixes.

Proof

Use induction on the sum of the prefix lengths. An empty prefix gives length . All recursive dependencies have smaller total prefix length.

Matching last letters

Assume . Appending this common letter to an LCS of the two shorter prefixes gives

Conversely, take an LCS of the current prefixes and remove its last letter. The remaining letters occur strictly before the final matched positions in both strings, hence in and :

Both bounds force equality. Thus matching the last letters is at least as good as dropping either one.

Different last letters

Assume . A common-subsequence alignment cannot use both final positions: both would have to represent its last letter, but the letters differ. Hence every alignment omits at least one final position:

This gives the upper bound by their maximum. Conversely, a common subsequence of either smaller prefix pair remains feasible for the current pair. The maximum is therefore also attainable.

Subproblem Reuse

The diagram shows the dependencies of a recursive call. In the mismatch case, both branches can reach the same smaller prefix pair; memoisation avoids computing it twice.

Complexity

With memoisation or bottom-up table filling,

The time complexity is for non-empty inputs; an empty input returns immediately. The memo table uses space, with at most recursive stack depth.

Bottom-up evaluation can retain only the previous and current rows, reducing auxiliary space to when only the length is required. Without memoisation, the recursion can take exponential time because subproblems reappear.

Examples

Prefix table for abc and ac

Let and . Each entry is the LCS length of its row and column prefixes:

Prefix of S / prefix of Taac
000
a011
ab011
abc012

The final letters match, so the final entry is

A longest common subsequence is .