Every time you run git diff, inspect a pull request on GitHub, or compare two versions of a legal contract, you are relying on text difference algorithms. At their core, diff engines solve a fundamental computer science question: given two sequences of characters or lines, what is the smallest possible set of insertions and deletions required to transform sequence A into sequence B?
While the problem sounds intuitive, finding an optimal edit script between two large documents is computationally intensive. In 1986, Eugene W. Myers published a breakthrough paper titled "An O(ND) Difference Algorithm and Its Variations", which revolutionized version control and remains the default diff engine in Git, Mercurial, and modern web applications. In this guide, we explore the graph-theoretic foundations of the Myers algorithm, contrast it with Levenshtein distance, and examine how client-side tools like our Text Compare achieve instant, side-by-side diffing in the browser.
1. Diff Theory: The Shortest Edit Script (SES) Problem
Formally, the difference between two sequences A of length N and B of length M is defined as finding a Shortest Edit Script (SES). An edit script consists of two basic atomic operations:
- Delete: Remove an element from sequence A.
- Insert: Add an element from sequence B.
Finding the Shortest Edit Script is dual to finding the Longest Common Subsequence (LCS). If you know the largest sequence of elements that appear in both A and B in the same relative order, the minimal edits are simply deleting whatever in A is not in the LCS and inserting whatever in B is not in the LCS.
2. The Edit Graph: Myers' Breakthrough
Myers transformed the string comparison problem into a directed graph search. Imagine a grid where sequence A is mapped along the horizontal X-axis (from 0 to N) and sequence B is mapped along the vertical Y-axis (from 0 to M).
In this grid, you can move between vertices (x, y) using three types of edges:
- Horizontal step (right): Moves from
(x, y)to(x + 1, y). This represents a deletion from sequence A (cost = 1). - Vertical step (down): Moves from
(x, y)to(x, y + 1). This represents an insertion from sequence B (cost = 1). - Diagonal step (down-right): Moves from
(x, y)to(x + 1, y + 1). This occurs if and only ifA[x] === B[y]. Because both elements match, this move represents retaining the character and incurs zero cost (cost = 0)!
Graph Search Formulation
The problem of finding the minimal edit script is mathematically identical to finding the shortest path from vertex (0, 0) to vertex (N, M) in the edit graph, where horizontal and vertical edges cost 1, and diagonal edges are completely free (cost = 0).
3. Why Myers Algorithm is O(ND)
A standard breadth-first search (BFS) or dynamic programming matrix (like Wagner-Fischer) requires O(N * M) time and space. For two 10,000-line source code files, an O(N * M) algorithm requires allocating a matrix of 100,000,000 cells, causing severe memory pressure and browser freezes.
Myers recognized that in practice, two versions of a document or source file are mostly identical: the number of actual differences (denoted as D) is typically tiny compared to the total length N. Myers' algorithm iterates over the number of differences D = 0, 1, 2, ... and computes the furthest reaching path along each diagonal k = x - y.
Because the algorithm halts as soon as it reaches (N, M) at depth D, its time complexity is O(N * D). When two files are nearly identical (D is small), the Myers algorithm runs in near-linear time O(N), calculating diffs in a few milliseconds.
4. Myers Diff vs. Levenshtein Distance
Developers often confuse Levenshtein distance with the Myers diff algorithm. While related, they serve fundamentally different use cases:
| Metric | Levenshtein Distance | Myers Diff Algorithm |
|---|---|---|
| Allowed Operations | Insert, Delete, Substitute (Replace) | Insert, Delete only |
| Granularity | Typo correction, fuzzy search, spellcheck | Line-by-line file changes, version control |
| Time Complexity | O(N * M) | O(N * D) (Linear when D is small) |
| Output | Single scalar integer (edit distance) | Full edit script with line-by-line ranges |
5. JavaScript Implementation: Token-Level Diffing
Below is a simplified implementation of the core Myers forward search in modern JavaScript. It computes the furthest-reaching paths along diagonal k:
function myersDiff(a, b) {
const n = a.length;
const m = b.length;
const max = n + m;
const v = { 1: 0 };
const trace = [];
for (let d = 0; d <= max; d++) {
trace.push({ ...v });
for (let k = -d; k <= d; k += 2) {
// Decide whether to step down or step right
let x;
if (k === -d || (k !== d && v[k - 1] < v[k + 1])) {
x = v[k + 1]; // vertical step (insert)
} else {
x = v[k - 1] + 1; // horizontal step (delete)
}
let y = x - k;
// Follow the diagonal as far as elements match (cost = 0)
while (x < n && y < m && a[x] === b[y]) {
x++;
y++;
}
v[k] = x;
// Reached target (N, M)
if (x >= n && y >= m) {
return reconstructPath(trace, a, b, d, k);
}
}
}
return [];
}
6. Word-Level vs. Line-Level Diffs in Browser UIs
When presenting diffs to human readers, displaying only line-level changes is insufficient. If a developer edits a single variable name inside a 100-character line, highlighting the entire line as deleted and replaced forces the user to manually scan the text to find the change.
Modern diff viewers employ a two-pass strategy:
- First Pass (Line-Level Diff): Run the Myers algorithm over array splits of
text.split('\n')to identify which blocks of lines were inserted, deleted, or unchanged. - Second Pass (Intra-Line Character Diff): For adjacent deletion and insertion pairs, run a second, character-level or word-level Myers pass. This enables highlighting the exact modified words inside the line using distinct background hues (e.g., light red for the line, dark red for the removed characters).
Conclusion
The Myers diff algorithm is a masterclass in algorithmic efficiency, demonstrating how mapping a combinatorial problem onto graph geometry can transform an expensive O(N * M) computation into an agile O(ND) operation. By bringing this algorithm directly into the browser, developers and writers can compare documents, code snippets, and JSON payloads with absolute privacy and zero server dependence.
To compare text, code, or data side-by-side with real-time character-level highlights and zero data retention, try our free Text Compare tool.