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 T a ac 0 0 0 a 0 1 1 ab 0 1 1 abc 0 1 2 The final letters match, so the final entry is
A longest common subsequence is .