intermediate8 min read·Updated October 5, 2026

Longest Common Subsequence Explained: Tracing 'abcde' and 'ace'

Master the longest common subsequence algorithm through a visual grid walkthrough of 'abcde' and 'ace'. Build a mental model for dynamic programming.

By Learnisim AI·Published October 5, 2026
LEARNING ARCSetup → Model in the example → Full trace → Twist → Edge cases → Apply
PREREQUISITES
  • short strings
  • basic 2D arrays
Longest Common Subsequence (LCS) — DP Grid & Backtracking Comparing text1 = "abcde" & text2 = "ace" (LCS = "ace", length 3) 1. Substring vs Subsequence Problem text1: abcde text2: a c e Non-contiguous match 'b' and 'd' skipped Order preserved: "ace" 2. Total Disjointness Example ("abc" vs "def") text1: abc text2: def Result: 0 common characters in order (LCS length = 0) 3. DP Table (6 × 4 Grid) Match (+1 diag) | No match (max up/left) - a c e - a b c d e 0 0 0 0 0 1 1 1 0 1 1 1 0 1 2 2 0 1 2 2 0 1 2 3 How DP Transition Works: • Match (char1 == char2): dp[i][j] = dp[i-1][j-1] + 1 • No Match: dp[i][j] = max(up, left) Backtracking (Yellow Dashed): Start at bottom-right (3). Follow diagonal steps on matches: Reconstructed LCS: 'a' → 'c' → 'e' 4. Core Takeaways Time & Space Complexity O(n × m) time and O(n × m) space for DP table. Why Substrings Fail Substrings require continuous adjacency. Subsequences allow skipping gaps ('b', 'd'). Real-World Applications • Diff utilities (Git diff) • DNA sequence alignment & bioinformatics
LCS of "abcde" and "ace", then "abc" and "def" overview diagram
Why

Why simple string matching breaks for non-contiguous overlap

Phase 1: The gap problem in string comparison

Imagine you are comparing two DNA strands, two text revisions, or two user search queries to find what they share. Given text1 = "abcde" and text2 = "ace", your first instinct might be to look for a continuous block of matching characters. But a standard substring search fails: "abcde" contains "a", "b", "c", "d", and "e", and "ace" contains "a", "c", and "e". While they share "a", "c", and "e", those letters appear in that exact relative order without needing to be right next to each other.
If you checked for a contiguous substring, you would find at best a length of 1 (like "a" or "c") because the letter "b" breaks the contiguous block between "a" and "c" in the first string. Yet intuitively, "ace" feels like a much stronger match that preserves the sequence order. Without a method to skip over insertions and deletions while preserving left-to-right order, algorithms miss these scattered structural similarities.
Now consider a second contrast: comparing text1 = "abc" and text2 = "def". Here, no characters match in order at all, yielding an expected result of 0. How do we systematically distinguish between a partial interleaved overlap like "ace" and total disjointness like "def" without checking every single exponential combination of skipped letters?
Why Substring Matching Breaks: Contiguous vs. Subsequence (LCS)Example 1: The Gap Problem (text1 = "abcde", text2 = "ace")Contiguous Substring Search: Fails due to 'b' and 'd' gaps. Max length = 1.Text 1:abcdeText 2:aceSubstring match stops at 'b'! Max length = 1Longest Common Subsequence (LCS):Skips 'b' and 'd' while preserving order.Result: "ace" (Length = 3)Why Subsequence WinsPreserves relative left-to-right orderTolerates insertions & deletions in textCrucial for DNA diffs, git, and searchKey Takeaway:Substrings require contiguous memory blocks.Subsequences allow skipping gaps flexibly.Example 2: Total DisjointnessComparing text1 = "abc" and text2 = "def"text1: a - b - ctext2: d - e - fNo matching characters in order:LCS Length = 0 (Correctly detects zero overlap)
Why simple string matching breaks for non-contiguous overlap diagram
Model

The Subsequence Grid: Visualizing Choices Across Two Strings

Phase 2: The 2D Grid Model

To understand how to track overlapping characters without requiring them to sit side-by-side, we must picture a two-dimensional grid. Imagine placing text1 = "abcde" along the columns and text2 = "ace" along the rows of a matrix. Each cell in this grid represents a decision point: when we compare a character from "abcde" against a character from "ace", what is the length of the longest common subsequence up to that exact pair of indices?
If the character from text1 matches the character from text2, we take the diagonal history from the top-left cell and add 1. If they do not match, we take the maximum value from either the cell directly above us or the cell directly to our left. This grid formulation transforms a messy search problem into a structured path-finding exercise across a matrix of size .
For our locked example, the top row and left column start entirely at zero, acting as the boundary condition when one of the strings is empty.
Try this: Given text1 = "abcde" and text2 = "ace", sketch the dimensions of the comparison grid and explain what cell (0,0) represents.
The Subsequence Grid: Visualizing Choices Across Two Strings LCS DP Table for text1 = "abcde" and text2 = "ace" DP Matrix (6 cols x 4 rows) ε a b c d e ε a c e 0 0 0 0 0 0 0 1 1 1 1 1 0 1 1 2 2 2 0 1 1 2 2 3 Match: Diagonal + 1 Mismatch: max(Top, Left) Result LCS Length = 3 ("ace") The Two Core Transitions If text1[i] == text2[j] (Match!) dp[i][j] = dp[i-1][j-1] + 1 (Diagonal) If text1[i] != text2[j] (Mismatch) dp[i][j] = max(dp[i-1][j], dp[i][j-1]) Boundary & Why It Matters Cell (0,0) represents empty strings Base length is always 0 when comparing ε. What about "abc" vs "def"? No character matches anywhere in common. LCS("abc", "def") = 0 Grid fills entirely with 0s and 1s; max value = 0
The Subsequence Grid: Visualizing Choices Across Two Strings diagram
Worked example

Tracing the LCS Grid for text1 = "abcde" and text2 = "ace"

Phase 3: Building the DP Table

Now we take the 2D grid model from the previous phase and execute it step by step on our first running example: text1 = "abcde" and text2 = "ace". We will build a matrix dp of size to track the length of the longest common subsequence at every prefix combination.

Given

- text1 = "abcde" (length , rows to 5)
- text2 = "ace" (length , columns to 3)
- Base case: dp[i][0] = 0 and dp[0][j] = 0 for all .

Steps

1. Initialize the Grid: Create a table initialized with zeros to account for empty prefix comparisons.
2. Row 1 (text1[0] = 'a'):
- Compare with text2[0] = 'a': Match! dp[1][1] = dp[0][0] + 1 = 1.
- Compare with text2[1] = 'c' and text2[2] = 'e': No match, carry forward max left or up dp[1][2] = 1, dp[1][3] = 1.
3. Row 2 (text1[1] = 'b'):
- Compare with 'a', 'c', 'e': No matches in this row. Values copy down from above row 2 remains [0, 1, 1, 1].
4. Row 3 (text1[2] = 'c'):
- Compare with text2[0] = 'a': No match dp[3][1] = 1.
- Compare with text2[1] = 'c': Match! dp[3][2] = dp[2][1] + 1 = 2.
- Compare with text2[2] = 'e': No match dp[3][3] = max(dp[3][2], dp[2][3]) = 2.
5. Row 4 (text1[3] = 'd'):
- No matches with 'a', 'c', or 'e'. Row 4 copies row 3 [0, 1, 2, 2].
6. Row 5 (text1[4] = 'e'):
- Compare with text2[0] = 'a' and text2[1] = 'c': No new matches dp[5][1] = 1, dp[5][2] = 2.
- Compare with text2[2] = 'e': Match! dp[5][3] = dp[4][2] + 1 = 2 + 1 = 3.

Result

The bottom-right cell dp[5][3] contains 3, which is the length of the longest common subsequence ("ace").
Try this: Using the same dynamic programming recurrence, trace the resulting bottom-right value for text1 = "abc" and text2 = "def". What is the final value of dp[3][3]?
Phase 3: Tracing DP Table for text1 = "abcde" & text2 = "ace" - a c e - 0 0 0 0 a 0 1 1 1 b 0 1 1 1 c 0 1 2 2 d 0 1 2 2 e 0 1 2 3 Recurrence Rule: If text1[i] == text2[j]: dp[i][j] = dp[i-1][j-1] + 1 If text1[i] != text2[j]: dp[i][j] = max(left, up) Key Highlights: Matches highlight diagonal transitions (+1) Mismatches inherit max(left, up) Bottom-right cell dp[5][3] = 3 LCS("abcde", "ace") = 3 ("ace") Try This: Trace text1 = "abc" & text2 = "def". What is the final bottom-right value dp[3][3]?
Tracing the LCS Grid for text1 = "abcde" and text2 = "ace" diagram
Practice

Practice: Predicting DP State Changes for Alternative Inputs

Phase 4: Practice

Now that you have traced the overlapping case for text1 = "abcde" and text2 = "ace", let us test your understanding on a completely disjoint pair where no characters match: text1 = "abc" and text2 = "def".
Recall the core transition rule from the previous phase:
Mentally construct or sketch the dynamic programming table (including the initial zero-row and zero-column) for text1 = "abc" and text2 = "def".
The Question:
What will be the final value stored in the bottom-right cell dp[3][3] of the matrix, and why does this value reflect the longest common subsequence for text1 = "abc" and text2 = "def"?
Apply

Applying LCS Patterns to Real-World Text Diffing

Phase 5: Applying the Pattern Beyond Toy Strings

Now that you have traced the 2D dynamic programming grid for text1 = "abcde" and text2 = "ace" and verified the zero-match behavior for text1 = "abc" and text2 = "def", let us see where this abstract structure appears in the wild. The exact same DP matrix that pairs characters without requiring contiguous blocks is the engine behind modern file comparison tools like git diff and biological sequence alignment in bioinformatics. Instead of comparing single characters like 'a', 'c', and 'e', line-based diff utilities treat entire text lines as atomic elements in our sequences.
Imagine you are building a simplified version of git diff to compare two configuration files. File A has three lines: a database URL, a port number, and a timeout limit. File B reorders them and updates the port number. By mapping each line to an element in our 1D or 2D array, the longest common subsequence reveals which lines remained untouched across edits, while the omitted indices highlight deletions and insertions. You are no longer just counting lengths; you are reconstructing the history of edits by walking backward from the bottom-right cell of your grid.
When adapting this algorithm to new domains, the primary task is defining your equivalence relation. In our toy example, equality meant strict character identity (). In real-world applications, equality might mean matching normalized strings, case-insensitive tokens, or genomic base pairs with allowed mutation thresholds. The recurrence relation remains identical, but your base comparison function changes to suit the domain data.

FAQ

What is the LCS of 'abcde' and 'ace'?
The longest common subsequence is 'ace', giving a length of 3. Characters do not need to be contiguous, but they must appear in the same relative order.
What happens if two strings have no characters in common, like 'abc' and 'def'?
The longest common subsequence length is 0, since no characters overlap between the two strings.
What is the time and space complexity of the standard LCS dynamic programming solution?
The time complexity is O(m × n) and space complexity is O(m × n) for two strings of lengths m and n, though space can be optimized to O(min(m, n)).
How is a subsequence different from a substring?
A substring requires contiguous characters from the original string, whereas a subsequence allows characters to be skipped as long as their relative order is preserved.

Keep learning