The longest sequence appearing (not necessarily contiguously) in both strings.
The recurrence
int lcs(const string& a, const string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
dp[i][j] = (a[i-1] == b[j-1]) ? dp[i-1][j-1] + 1
: max(dp[i-1][j], dp[i][j-1]);
return dp[n][m];
}time and space; space with a rolling array.
Reconstructing the subsequence
string reconstruct(const string& a, const string& b, vector<vector<int>>& dp) {
string res;
int i = a.size(), j = b.size();
while (i > 0 && j > 0) {
if (a[i-1] == b[j-1]) { res += a[i-1]; i--; j--; }
else if (dp[i-1][j] >= dp[i][j-1]) i--;
else j--;
}
reverse(res.begin(), res.end());
return res;
}Needs the full table. With only two rows, use Hirschberg’s algorithm — time, space, and it still produces the actual subsequence.
Beating
| Situation | Method | Time |
|---|---|---|
| Both strings are permutations | map to indices, run LIS | |
| One string has few distinct characters | Hunt-Szymanski | , = matching pairs |
| Small alphabet, need raw speed | bit-parallel (Crochemore-Iliopoulos-Pinzon) | |
| The answer is known to be short/long | banded DP around the diagonal |
The permutation trick
If and are permutations of the same multiset with distinct elements, replace each element of by its position in . A common subsequence of the two is exactly an increasing subsequence of that new array. instead of .
Lower bound: no algorithm exists for general LCS unless the Strong Exponential Time Hypothesis fails. The quadratic barrier is real.
The related family
| Problem | Difference |
|---|---|
| Longest common substring (contiguous) | dp[i][j] = dp[i-1][j-1]+1 on match, 0 otherwise; or use a suffix automaton for |
| Edit distance | minimisation with insert/delete/replace |
| Shortest common supersequence | |
| Longest palindromic subsequence | LCS of and |
| Minimum insertions to make a palindrome | |
| LCS of strings | — NP-hard in |
| Longest common increasing subsequence | with a running maximum |
| Diff (Myers’ algorithm) | LCS with an algorithm where is the edit distance — what git diff uses |
The palindromic subsequence reduction is the most reused: LPS = LCS, so any LCS code solves palindrome problems for free.
Counting distinct LCS
Add a count array alongside the length array, being careful not to double-count when both dp[i-1][j] and dp[i][j-1] achieve the maximum — subtract dp[i-1][j-1]’s count by inclusion-exclusion when the characters differ and both branches tie.
See also: Edit Distance · LIS · Hirschberg