Algorithm

Word Ladder

Breadth-First Search (BFS) Pattern

Word Ladder

Given two words beginWord and endWord and a list of words wordList, find the shortest transformation sequence from beginWord to endWord.

Each step may change exactly one letter, and every word produced along the way must appear in wordList. beginWord itself does not need to be in wordList, but endWord must be, otherwise no sequence exists.

Return the number of words in the shortest such sequence, counting both beginWord and endWord, or 0 if no sequence exists. So a single valid step from beginWord to endWord returns 2.

CONSTRAINTS
  • 1 <= beginWord.length <= 10
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 5000, and every word has the same length
  • All words consist of lowercase English letters
  • beginWord != endWord, and all words in wordList are distinct
EXAMPLE 1
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
The sequence hit, hot, dot, dog, cog contains 5 words and each consecutive pair differs in exactly one position. The alternative through lot and log is also 5 words long — several shortest sequences may exist, but only the count is returned.
EXAMPLE 2
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: 0
Every intermediate word must come from the list, and that includes the final word. Since cog is absent, no valid sequence can end there, regardless of how close dog is to it.
EXAMPLE 3
Input: beginWord = "a", endWord = "c", wordList = ["a","b","c"]
Output: 2
With single-letter words, changing the one letter turns a straight into c, and c is in the list. The sequence a, c holds 2 words. The presence of b is irrelevant since no detour is needed.
EXAMPLE 4
Input: beginWord = "hot", endWord = "dog", wordList = ["hot","dog"]
Output: 0
hot and dog differ in two positions, so they are not one step apart, and the list contains no word that could bridge them — dot or hog would have done it. Being in the list is not enough; a chain of single-letter steps must exist.
Does the returned number count words or transformations?
Words, including both ends. A single transformation returns 2, not 1. Getting this off by one is the most common failure on this problem, so it is worth restating back to the interviewer.
Must beginWord be present in wordList?
No, and it is often absent. endWord, on the other hand, must be present — checking for it first lets you return 0 immediately instead of searching a space with no reachable goal.
Can a word be reused later in the sequence?
There is never a reason to. Revisiting a word means the sequence contains a loop, and deleting the loop gives a strictly shorter sequence — so a shortest sequence never repeats a word. That reasoning is also what makes it safe to mark words as used and never look at them again.
Do I need to return the actual sequence of words?
Only the length here. Returning every shortest sequence is a genuinely harder follow-up, since you would have to record all predecessors of each word rather than just the first one found.

Nothing in this statement mentions a graph, a grid, or a distance. It talks about words and letters. So the first real task is not to pick an algorithm but to notice that the problem is a shortest-path problem in disguise, and that recognition is the skill worth extracting.

Seeing the graph that is not drawn

A graph is nothing more than a set of things plus a rule about which pairs are directly connected. Apply that definition literally here.

- What are the things? The words. Each word is one node.
- What is the connection rule? Two words are joined when they differ in exactly one position.
- What is the cost of following a connection? One transformation, always — every edge costs the same.

With that in place, "shortest transformation sequence" reads as "shortest path from beginWord to endWord", and the equal-cost property is exactly the condition under which the simplest shortest-path method works.

The unusual part, compared to a grid problem, is that nobody hands you the edges. In a grid the neighbours of a cell are obvious from its coordinates. Here they must be computed, and how you compute them turns out to dominate the running time. This is what people mean by a state space problem: the graph is implied by a rule, it is generated as you walk it, and it is never built or stored in full.

The layer argument

The search strategy follows from the equal-cost property. Consider all words one step from beginWord — call that layer 1. Layer 2 is every unused word one step from layer 1, and so on. A word first appears in the layer matching its true minimum distance: it cannot appear earlier, since that would mean a shorter chain exists, and it cannot appear later, because as soon as any layer-d word is expanded, all of its unused neighbours are claimed for layer d+1.

So the first time endWord appears, the count is final. There is no need to compare competing sequences and no best-so-far to maintain — discovery order is distance order. Expanding layer by layer like this is Breadth-First Search, and it needs a queue, which hands words back in the order they were found. Using a stack instead would drive the search deep down one chain before finishing the current layer, and the first arrival at endWord would no longer be the shortest.

How to find a word's neighbours

The direct approach is to compare the current word against every word in the list and keep the ones differing in a single position.

python
def neighbours(word, word_list):
    result = []
    for candidate in word_list:
        diff = sum(1 for a, b in zip(word, candidate) if a != b)
        if diff == 1:
            result.append(candidate)
    return result

Count the cost. Each comparison examines L letters, there are N words to compare against, and this runs once per word expanded, so the search totals O(N² × L). With N at 5000 and L at 10, that is around 250 million letter comparisons — slow, and it scales badly in exactly the dimension the constraints allow to grow.

The waste is specific: the vast majority of those comparisons find words differing in five or six positions, which were never plausible neighbours. We are scanning a whole dictionary to find items that a rule could have described directly.

Generating neighbours instead of searching for them

Turn it around. Rather than asking which existing words are one letter away, construct every string that is one letter away and test which of them are real words. For a word of length L there are exactly L × 25 such strings, and testing membership in a set — a structure that answers "is this present?" by hashing the value to a slot rather than scanning — costs constant time.

python
from collections import deque

def ladder_length(begin_word, end_word, word_list):
    words = set(word_list)
    if end_word not in words:
        return 0

    queue = deque([(begin_word, 1)])        # the sequence so far contains 1 word
    words.discard(begin_word)

    while queue:
        word, length = queue.popleft()
        if word == end_word:
            return length

        for i in range(len(word)):
            for ch in "abcdefghijklmnopqrstuvwxyz":
                candidate = word[:i] + ch + word[i + 1:]
                if candidate in words:
                    words.remove(candidate)          # claim it: never queue it twice
                    queue.append((candidate, length + 1))
    return 0
Crucial Notethe word is removed from the set at the moment it is discovered, not when it is later popped. Deleting from the set is doing double duty — it marks the word as visited and it prevents any other word in the same layer from queueing it again. Skip this and a word reachable from four different predecessors enters the queue four times, each copy expanding the identical subtree. On a dense dictionary that turns a linear search into an exponential one. The removal is safe precisely because of the FAQ argument above: a shortest sequence never revisits a word, so a word claimed at layer d can never be usefully reached at a later layer.

The cost per word expanded is L positions × 26 letters × O(L) to build each candidate string, so O(L² × 26), and with N words in total the search is O(N × L² × 26). For the given limits that is a few million operations instead of hundreds of millions.

Worked example:hit to cog

Dictionary {hot, dot, dog, lot, log, cog}. Distances are counts of words in the sequence, so beginWord starts at 1.

- Layer 1. The queue holds (hit, 1). Popping it, the generator tries ait, bit, …, then h_t with every letter, then hi_. Of all 75 candidates only hot is in the set. Claim it. Queue: [(hot, 2)].
- Layer 2. Pop (hot, 2). Changing the first letter gives dot and lot, both present, both claimed. Changing the second gives hat, hbt and so on — none present. Changing the third gives hop, hob and so on — none present. Queue: [(dot, 3), (lot, 3)].
- Layer 3. Pop (dot, 3). Its live neighbours are lot, already claimed and therefore skipped, and dog, which is claimed now. Pop (lot, 3), which yields log. Queue: [(dog, 4), (log, 4)].
- Layer 4. Pop (dog, 4). Changing the first letter gives cog, which is in the set. Claim it, queue it at 5. Pop (log, 4), which also generates cog — but cog was removed from the set when dog claimed it, so nothing is queued and no duplicate appears. Queue: [(cog, 5)].
- Layer 5. Pop (cog, 5). It equals endWord. Return 5.

The sequence found is hit, hot, dot, dog, cog. The path through lot and log is equally short, and the algorithm simply reports the same count without caring which one it traced.

Now the second example, with cog absent from the list. The guard at the top fires immediately and returns 0 without any search. Without that guard the algorithm would still return 0, but only after exhausting the entire reachable component — correct but wasteful, and interviewers notice the check.

Halving the work: searching from both ends

There is one more gear, and it is the standard follow-up. BFS from a single origin expands a ball of radius d. If each word has roughly b neighbours, that ball holds on the order of b^d words, which grows brutally with distance.

Now run two searches at once — one forward from beginWord, one backward from endWord — and expand whichever frontier is currently smaller. They meet in the middle, so each has only travelled about d/2 layers, giving roughly 2 × b^(d/2) words examined instead of b^d. For b = 10 and d = 6 that is a few thousand instead of a million.

The bookkeeping is simple: keep two sets of frontier words, and at each round expand the smaller one and test whether any generated word appears in the opposite frontier. When one does, the two half-lengths sum to the answer. Bidirectional search is valid here for a specific reason worth stating — the "differs by one letter" relation is symmetric, so a step is traversable in both directions. On a directed graph with one-way edges you would need the reverse adjacency to search backwards at all.

The transferable checklist

The grid problems in this section hand you the graph. This one does not, and that is the whole lesson. When a statement describes configurations and legal moves between them — words and letter swaps, a lock and wheel turns, a board and piece slides, a number and arithmetic operations — run the same three questions:

- What is a node? One complete configuration, not a piece of one.
- What is an edge? One legal move, and can you generate all of them from a configuration without consulting a list?
- Does every edge cost the same? If yes, BFS gives the minimum. If no, you need Dijkstra.

Then add the two guards that make it work: a set for constant-time membership, and claiming states at discovery rather than at expansion.

Train the reflex: the phrase minimum number of moves to turn X into Y is a shortest-path problem no matter what X and Y are made of. Your job is to name the nodes.

Interactive Strategy Visualization
Phase 1 — Setup
Layer-by-Layer Word Transformation
SEARCH
1 Explored
1 in Queue
Search Frontier (1)hit (1)

Start: We begin with "hit" and want to reach "cog" by changing one letter at a time.

STEP 1/15
O(N² × L) Compare All Word Pairs
O(N × L² × 26) BFS With Generated Neighbours
O(N × L²) Bidirectional BFS