Edit Distance

When your phone suggests “kitten” for “sitting”, or a search engine asks “did you mean…”, it is measuring how many edits of a single letter separate two words. That number is the edit (Levenshtein) distance, and it is LCS’s closest relative.

intermediate⏱ 10 min read
01

Learn

The idea, the mechanics and the cost.

01Three operations, one number

You may insert a letter, delete a letter, or substitute one letter for another, each at cost 1. The edit distance from A to B is the fewest operations that turn A into B.

KITTEN → SITTING takes 3: substitute K → S, substitute E → I, insert G at the end. The figure lines the two words up so you can see it: equal letters stacked on each other cost nothing, mismatched pairs are substitutions, and a letter facing a gap is an insertion (or a deletion in the other direction).

Every way of editing A into B is such an alignment, so the question becomes: which alignment has the fewest mismatches and gaps? Trying them all is exponential. As with LCS, the way in is to ask the question about prefixes.

KITTEN → SITTING: 3 editsKITTEN–SITTINGsubstitute ×2insert ×1- - keep

02The recurrence: three arrows into each cell

Let d[i][j] be the distance between the first i letters of A and the first j letters of B.

Base cases: d[i][0] = i (delete everything) and d[0][j] = j (insert everything).

For the rest, look at the last letters A[i] and B[j]:

- if they are equal, keep them for free: d[i][j] = d[i−1][j−1]; - otherwise pay 1 for the best of three moves: substitute (diagonal) d[i−1][j−1], delete A[i] (from above) d[i−1][j], insert B[j] (from the left) d[i][j−1].

So d[i][j] = 1 + min(diag, up, left) on a mismatch. Compare with LCS: the same table, the same three neighbours, but min of costs instead of max of lengths, and a diagonal move is now allowed even when the letters differ.

i−1, j−1i−1, ji, j−1i, jsubstitute +1(equal letters: +0)delete +1insert +1d[i][j] = min of the three arrows

03Filling, walking back, and where it is used

Fill the (m+1) × (n+1) table row by row; the bottom right cell is the answer. For KITTEN → SITTING it is 3.

To list the operations, walk back from the corner exactly like LCS: a diagonal step with equal letters is “keep”, a diagonal step with different letters is “substitute”, a step up is “delete”, a step left is “insert”. Collect them and reverse.

Cost: O(m·n) time; for the distance alone, two rows give O(n) memory.

Variations come from changing the weights or the moves: only insert and delete gives m + n − 2·LCS (so LCS solves it); allowing swaps of neighbouring letters gives the Damerau distance; giving a substitution a different price gives sequence alignment scores used in biology. The table and the walk back never change.

d[i][j] = distance between the first i letters of A and j of BεSITTINGεKITTEN01234567112345672212345633212345443212345543223466543323operations:K → Skeep I, T, TE → Ikeep N+ Gd[6][7] = 3

Cost at a glance

Fill the tableO(m·n)
Distance only, two rowsO(n) memory
Recover the operationsO(m + n)

Remember

  1. d[i][j] is the distance between prefixes; the border is i or j because an empty prefix needs that many inserts or deletes.
  2. Equal letters copy the diagonal; otherwise take 1 + min of diagonal (substitute), up (delete) and left (insert).
  3. It is the same table skeleton as LCS, with costs and min instead of lengths and max.
02

Play

Step through the algorithm, then try it on your own input.

👀 What to watch: Each mismatch cell shows its three candidates (substitute, delete, insert). After filling, the walk back lists the operations in order. Try HORSE → ROS.

Interactive visualizerfocus here, then space ← →
✎ Your inputTwo words of up to 9 letters, e.g. HORSE → ROS.

Edit distance: KITTEN → SITTING

εSITTING
ε01234567
K1·······
I2·······
T3·······
T4·······
E5·······
N6·······

Operations

∅
Edit distance: fewest insert/delete/substitute operations to turn KITTEN into SITTING. Row 0 = insert everything; column 0 = delete everything.

Pseudocode

 1 d[i][0]=i (delete all), d[0][j]=j (insert all) 2 if A[i]==B[j]: d[i][j] = d[i-1][j-1]        // keep 3 else: d[i][j] = 1 + min(diag=substitute, up=delete, left=insert) 4 reconstruct: follow the arrows back, listing operations
1 / 1
03

Check

Three questions. Pick an answer to see why.

Q1

What is the edit distance from HORSE to ROS?

Q2

In the table, a step from (i, j) back to (i−1, j) means which operation?

Q3

What is d[0][5]?

04

Practice

Real problems to lock it in, easiest first.