Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 25 – Dynamic programming: knapsack, grids and strings

Edit distance with operations

The fewest insertions, deletions and replacements that turn one string into another, with the operations listed, from a typed city name to the official one.

  • Intermediate
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25.0.4.1, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 6 of 35 minutes free
Free: The problem: typed names against an official list · The brute force, and why it fails at these sizes · What to notice. With a pass: the other 14 sections and 3 interview answers, about 29 minutes.

What you will learn

  • Compute the Levenshtein distance with insert, delete and replace
  • Rebuild the list of operations in a fixed, testable order
  • Prove the recurrence from the last column of an alignment
  • Modify the costs for weighted edits, insertions and deletions only, or swapped letters
  • Match typed names against a list, with a band of diagonals when only small distances matter

Before you start

What you’ll learn · Before you start

What you will learn

  • Compute the Levenshtein distance with insert, delete and replace
  • Rebuild the list of operations in a fixed, testable order
  • Prove the recurrence from the last column of an alignment
  • Modify the costs for weighted edits, insertions and deletions only, or swapped letters
  • Match typed names against a list, with a band of diagonals when only small distances matter

Before you start

On this page

The edit distance between two strings is the smallest number of single-character edits, insertions, deletions and replacements, that turn one into the other; it is also called the Levenshtein distance. “Mysore” is 2 edits from “Mysuru” and “Hydrabad” 1 from “Hyderabad”, which is how a search box still finds the city someone meant. Interviews ask for the number first and then for the edits themselves. Both come from one table of (n + 1)(m + 1) cells, filled in O(nm) time for strings of n and m characters.

The problem: typed names against an official list

Problem A: the edit distance. Given strings a and b, return the fewest insertions, deletions and replacements of single characters that turn a into b; each edit costs 1. Limits: 0 ≤ n, m ≤ 1,000.

  • "mysore" to "mysuru": 2, two replacements.
  • "vishakapatnam" to "visakhapatnam": 2, one deletion and one insertion.
  • "" to "agra": 4 insertions.

Problem B: the operations. Also return one cheapest list of edits, first to last, each with its position in the typed name.

Two alignments: mysore over mysuru with two replaced letters, and vishakapatnam over visakhapatnam with one deletion and one insertion.mysore → mysuru: 2 replacementsmmyyssouRrreuRvishakapatnam → visakhapatnam: 2 editsvviisshDaakkhIaappaattnnaammR replace · D delete · I insert · line: keep

An edit script lines the two names up letter by letter

Text description of the diagram

Each name is drawn as a row of letter boxes, with the typed name on top and the official name below it, so that the letters an edit script keeps sit one above the other, joined by a short line.

  1. Mysore over Mysuru: m, y, s and r are kept. The o above u and the e above u are marked R, two replacements, so the edit distance is 2.
  2. Vishakapatnam over Visakhapatnam: the h after "vis" in the typed name has an empty dashed box below it, marked D, because it is deleted; further right, an h of the official name has an empty dashed box above it, marked I, because it is inserted. Every other letter is kept, so the distance is again 2: one deletion and one insertion.

The brute force, and why it fails at these sizes

Look at the first character of each string. Try keeping or replacing it, deleting it from a and inserting the character of b, solve what is left the same way, and take the cheapest of the three. Every call makes three more, so the number of calls explodes: 13,483 for two names of 6 letters, 897,625 for names of 8 and 9 letters, about 2.1 billion for two of 13 letters and 1.45 × 10²² for two of 30. The answers are right; the time is hopeless long before the limit of 1,000 characters.

Every edit tried at every step, with the calls counted Python · brute/brute_force.py
# Edit distance by brute force: at every pair of positions, try deleting, inserting and replacing (or keeping an
# equal letter), and take the cheapest. Every call is counted, so the growth shows on short names.
calls = 0


def distance(a, b, i=0, j=0):
    """The fewest edits turning a[i:] into b[j:], trying every operation at every step."""
    global calls
    calls += 1
    if i == len(a) or j == len(b):
        return (len(a) - i) + (len(b) - j)  # only insertions or only deletions are left
    keep_or_replace = distance(a, b, i + 1, j + 1) + (a[i] != b[j])
    delete = distance(a, b, i + 1, j) + 1
    insert = distance(a, b, i, j + 1) + 1
    return min(keep_or_replace, delete, insert)


def calls_needed(n, m):
    """How many calls the brute force makes on strings of n and m letters (the same for any letters)."""
    c = [[1] * (m + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            c[i][j] = 1 + c[i - 1][j - 1] + c[i - 1][j] + c[i][j - 1]
    return c[n][m]


for typed, city in [("Pune", "Poona"), ("Mysore", "Mysuru"), ("Kolkatta", "Kolkata"), ("Hydrabad", "Hyderabad")]:
    calls = 0
    d = distance(typed.lower(), city.lower())
    print(f"{typed} -> {city}: distance {d}, {calls:,} calls")
print(f"Vishakapatnam -> Visakhapatnam (13 letters each) would make {calls_needed(13, 13):,} calls")
print(f"two strings of 30 letters: {calls_needed(30, 30):.2e} calls")

Output

Pune -> Poona: distance 3, 1,021 calls
Mysore -> Mysuru: distance 2, 13,483 calls
Kolkatta -> Kolkata: distance 1, 162,817 calls
Hydrabad -> Hyderabad: distance 1, 897,625 calls
Vishakapatnam -> Visakhapatnam (13 letters each) would make 2,114,900,428 calls
two strings of 30 letters: 1.45e+22 calls

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py

What to notice

  • Two numbers describe every call: how many characters of a and of b are already used. That is at most (n + 1)(m + 1) different calls, about a million for two strings of 1,000, while the brute force makes billions on names of 13 letters.
  • Each edit moves those numbers by one. A deletion uses a character of a, an insertion one of b, and a replacement or a kept character one of each.

Wagner and Fischer’s table stores the answer for every pair of prefixes. A walk back through the table lists the edits. The same table takes other costs: weighted edits, insertions and deletions only, or a swap of two neighbouring letters as one edit. When only small distances matter, a band of diagonals around the main one is enough.

The Wagner–Fischer table Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Why the cheapest of three moves is right
  • A dry run on mysore and mysuru
  • Rebuilding the list of operations
  • The code in five languages
  • Changing the costs
  • Fewer cells when the distance is small: a band of diagonals
  • Swapped letters: optimal string alignment
  • Matching a typed name against a list
  • Edge cases and common bugs
  • Exercise: the closest city
    • The closest city to a typed name
  • A practice set with sample answers
  • Check yourself
  • Interview questions
    • Which edits does the Levenshtein distance count, and why is it never more than the length of the longer string?
    • A delivery app must match a typed locality name against 1,000,000 official names in under 50 ms, allowing up to 2 typing slips. Comparing it with every name through the full table is far too slow. What do you build?
    • Your function returns the edit operations, and a unit test compares them with a fixed list. After a refactor of the walk back the test fails, although the distance is unchanged and the new list is also correct. What happened, and how should the function and the tests change?
    • A team counts a swap of two neighbouring letters as one edit and stores names in a BK-tree, which prunes its search with the triangle inequality. Some searches now miss names that are within the search radius. Explain why, and fix it.
  • Key takeaways

1 diagram · 9 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 24 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.