Algorithm L: Levenshtein Edit Distance
Vladimir Levenshtein, 1965 ("Binary codes capable of correcting deletions, insertions, and reversals")
Given two strings $A$ and $B$, find the edit distance between them: the minimum number of single-character inserts, deletes, and substitutes needed to transform $A$ into $B$.
Worked example. $A = \texttt{kitten}$, $B = \texttt{sitting}$:
- $\texttt{kitten} \to \texttt{sitten}$ (substitute $\texttt{k} \to \texttt{s}$)
- $\texttt{sitten} \to \texttt{sittin}$ (substitute $\texttt{e} \to \texttt{i}$)
- $\texttt{sittin} \to \texttt{sitting}$ (insert $\texttt{g}$)
So $d(\texttt{kitten}, \texttt{sitting}) = 3$.
The algorithm produces this number without enumerating edit scripts. It builds a matrix where $dp[i][j]$ is the edit distance between $A$'s first $i$ characters and $B$'s first $j$ characters, then reads off $dp[|A|][|B|]$ as the answer.
Why character-level, not byte-level. UTF-8 encodes many scripts with multi-byte characters. A naive byte-level edit distance over those would over-count by the byte-per-character factor and produce nonsense.
For example, in Korean text: UTF-8 represents one Hangul syllable as three bytes. Comparing 안녕 (two syllables, six bytes) to 안녕하세요 (five syllables, fifteen bytes) at the byte level would report nine. The correct character-level answer is three.
The implementation backing the playground below operates on Rust's chars() iterator, which yields one Unicode codepoint per element, so multi-byte text scores correctly regardless of script.
The two-row optimization. A literal $|A| + 1$ by $|B| + 1$ matrix uses $\Theta(|A| \cdot |B|)$ memory.
For two long strings this is wasteful: each cell only depends on the previous row plus the cell to its left. Keeping just two rows ($\textit{prev}$ and $\textit{curr}$) reduces space to $\Theta(\min(|A|, |B|))$ at no cost in time.
The trace below shows both rows at every labeled state.
Computation Method
Why It Works
The recurrence asks: 'what was the LAST edit?' Any optimal edit script ends with exactly one of three operations: deleting the last character of $A$, inserting the last character of $B$, or substituting the last character of $A$ for the last character of $B$ (which is free if they already match). Each option reduces the problem to a smaller subproblem whose answer is already computed. The two-row optimization stores only $\textit{prev}$ (row $i-1$) and $\textit{curr}$ (row $i$) because $\textit{dp}[i][j]$ depends on three neighbors: $\textit{dp}[i-1][j]$, $\textit{dp}[i][j-1]$, and $\textit{dp}[i-1][j-1]$ — all in the previous row or one cell to the left in the current row.
Interactive Trace
Step through the dp matrix on real inputs. The active cell is outlined; the three contributors (delete, insert, substitute) are tinted to match the legend.