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 20 – Graph patterns: traversal

BFS over states: word ladders and padlocks

Solve word ladders and padlock puzzles with BFS over states: encode each state, find neighbours with wildcard buckets, and search from both ends at once.

  • 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, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 9 of 35 minutes free
Free: The problem: a ladder of four-letter words · The brute force: every ladder, and every pair of words · What to notice. With a pass: the other 11 sections and 3 interview answers, about 26 minutes.

What you will learn

  • Encode puzzle positions as hashable values or as integers
  • Generate neighbouring states with buckets of patterns or with substitutions
  • Find the fewest moves between two states with breadth-first search
  • Rebuild one shortest sequence of moves, or list every shortest one
  • Search from both ends at once with bidirectional BFS

Before you start

What you’ll learn · Before you start

What you will learn

  • Encode puzzle positions as hashable values or as integers
  • Generate neighbouring states with buckets of patterns or with substitutions
  • Find the fewest moves between two states with breadth-first search
  • Rebuild one shortest sequence of moves, or list every shortest one
  • Search from both ends at once with bidirectional BFS

Before you start

On this page

A word ladder turns one word into another by changing one letter at a time, with every word along the way taken from a list, and asks for the fewest changes. It is a shortest-path problem on a graph that nobody draws: every word is a node, two words are joined when they differ in exactly one position, and a breadth-first search from the start word finds the answer. The same BFS over states solves padlocks, jugs, sliding tiles and board moves, whenever a position can be written down as a value and every move costs the same.

The problem: a ladder of four-letter words

The problem

You are given a start word, a target word and a list of words, all lower-case and all of the same length L. A ladder begins with start, ends with target, and each word in it differs from the word before in exactly one position; every word after start must come from the list. Return the number of changes in the shortest ladder, 0 when start equals target, and −1 when no ladder exists. Limits: 1 ≤ L ≤ 10 and up to 50,000 words.

The lesson’s list has 32 words, from lamp and lame to kind and zinc. Three examples:

  1. From lamp to fire: 4 changes, through lame, fame and fare.
  2. From lamp to kind: 6 changes, for example through lame, lime, line, fine and find.
  3. From lamp to zinc: −1, because no word of the list differs from zinc in a single letter.
The words of the list in rows by fewest changes from lamp, 0 to 4, with the ladder lamp, lame, fame, fare, fire drawn thick.01234lamplamelimpfamelimecamelanegamefarefatelinecanemanegategalefirefineminewinemalethe ladderfirst reached from

The word list in layers, as BFS reaches it from lamp

Text description of the diagram

Every word that a breadth-first search reaches from "lamp" within 4 changes is drawn in the row of its distance, with a thin line to the word it was first reached from.

  1. Row 0: lamp.
  2. Row 1: lame and limp, both from lamp.
  3. Row 2: fame, lime, came, lane and game, all from lame.
  4. Row 3: fare and fate from fame; line from lime; cane from came; mane from lane; gate and gale from game.
  5. Row 4: fire from fare; fine, mine and wine from line; male from mane.

The ladder lamp, lame, fame, fare, fire is drawn with thick lines and shaded words: 4 changes, the fewest. The search reaches 20 words by row 4; of the other 12 words of the list, 11 are farther away and zinc is never reached at all.

The brute force: every ladder, and every pair of words

The first idea is to walk every ladder that repeats no word, depth first, and keep the shortest. The run counts them on the lesson’s list and on two smaller lists inside it:

Every ladder from lamp to fire, on lists of 22, 27 and 32 words Python · brute/ladders_brute.py
"""Every ladder from lamp to fire that repeats no word, found depth first: the count explodes as the list grows."""

WORDS = ["lamp", "lame", "lime", "limp", "line", "fine", "fire", "fare", "fame", "came", "cane", "lane", "mane",
         "mine", "mire", "wire", "wine", "wise", "rise", "rite", "fate", "gate", "game", "gale", "male", "mile",
         "mild", "wild", "wind", "kind", "find", "zinc"]


def one_change(a, b):
    return sum(x != y for x, y in zip(a, b)) == 1


def every_ladder(words, start, target):
    """(number of ladders, fewest changes, calls made): a depth-first walk over ladders without repeats."""
    nearby = {w: [v for v in words if one_change(w, v)] for w in set(words) | {start}}
    ladders, fewest, calls = 0, None, 0
    on_ladder = {start}

    def walk(word, changes):
        nonlocal ladders, fewest, calls
        calls += 1
        if word == target:
            ladders += 1
            fewest = changes if fewest is None else min(fewest, changes)
            return
        for nxt in nearby[word]:
            if nxt not in on_ladder:
                on_ladder.add(nxt)
                walk(nxt, changes + 1)
                on_ladder.discard(nxt)

    walk(start, 0)
    return ladders, fewest, calls


print("words in the list   ladders from lamp to fire   fewest changes   calls")
for size in (22, 27, 32):
    keep = WORDS if size == 32 else [w for w in WORDS if w not in (
        ("wise", "rise", "rite", "gate", "fate") + (("mane", "cane", "came", "game", "gale") if size == 22 else ()))]
    ladders, fewest, calls = every_ladder(keep, "lamp", "fire")
    print(f"{len(keep):>15} {ladders:>27,} {fewest:>16} {calls:>9,}")
n = 50_000
print(f"comparing every pair of {n:,} words to find the neighbours: {n * (n - 1) // 2:,} pairs")

Output

words in the list   ladders from lamp to fire   fewest changes   calls
             22                         599                4     3,087
             27                      26,091                4   152,822
             32                     146,590                4   993,929
comparing every pair of 50,000 words to find the neighbours: 1,249,975,000 pairs

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

Five more words multiply the count: 599 ladders on 22 words, 26,091 on 27 and 146,590 on 32, almost a million calls for a list that fits on a postcard. Even a search that visits each word once has a second cost hidden in it: finding a word’s neighbours by comparing it with every other word costs 1,249,975,000 pair comparisons on 50,000 words.

What to notice

The graph does not have to exist before the search. Each word’s neighbours can be produced when the search needs them: change each of the L positions to each other letter and look the result up in a set, or file every word under the L patterns that blank one of its positions, such as l*mp, so that the words sharing a pattern are exactly the neighbours through that position. Then the search touches each word once, and the fewest changes is the BFS distance. Padlocks work the same way: each code is a node, and turning one wheel one step up or down gives its neighbours.

Neighbours on demand: three ways, counted Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • The search with buckets, each read once
    • The code in five languages
  • Every shortest ladder, not just one
  • Bidirectional BFS: two smaller searches
  • The padlock: codes as numbers
  • Variants
  • Edge cases and common bugs
  • Exercise: fewest turns to open a padlock
    • Fewest turns to open a padlock
  • A practice set with answers
  • Check yourself
  • Interview questions
    • In a BFS over states, what must one state contain, and how can you tell that something is missing from it?
    • A word-ladder service must answer queries on a dictionary of 200,000 words of up to 12 letters, each within 50 ms. A BFS that compares every word with every other word is far too slow. Design the neighbour lookup, estimate the work per query, and say what you build in advance.
    • You must return every shortest ladder between two words. A first version keeps a whole partial ladder in the queue for every path it extends, and runs out of memory on a large dictionary. Why does that happen, and how do you fix it?
    • When does bidirectional BFS pay off, and when does it not? Estimate the saving on a graph where every state has 10 neighbours and the target is 8 moves away, then on a 4-wheel padlock, and name a case where it cannot be used.
  • Key takeaways

7 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 21 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.