150NeetCode 150

92.Word Ladder

HardGraphs · 13/19

Problem

A transformation sequence from beginWord to endWord is a list of words where each adjacent pair differs by exactly one letter, and every word after beginWord must appear in wordList.

Return the number of words in the shortest such sequence (including both beginWord and endWord), or 0 if no sequence exists. beginWord itself does not need to be in wordList.

Examples

Input:  beginWord = "hit", endWord = "cog",
        wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: hit -> hot -> dot -> dog -> cog has 5 words.
Input:  beginWord = "hit", endWord = "cog",
        wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: "cog" is not in the word list, so it can never be reached.

Constraints

  • 1 <= beginWord.length <= 10
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 5000
  • wordList[i].length == beginWord.length
  • All words consist of lowercase English letters; beginWord != endWord; words in wordList are unique.

💡 Key Insight

Treat every word as a node and connect two words if they differ by one letter. The question becomes shortest path in an unweighted graph, which is exactly what BFS answers. The only real design choice is how to find a word's neighbours quickly: comparing against all N words costs O(N · L), while trying all 26 · L single-letter substitutions and checking a hash set is far cheaper when N is large. Searching from both ends at once shrinks the explored frontier even further.

BFS Comparing All Words

TimeO(N² · L)SpaceO(N)

Intuition

Run BFS from beginWord, level by level. To expand a word, scan the whole word list and enqueue every unvisited word that differs in exactly one position. The first time we pop endWord, its level is the answer.

Algorithm

  1. If endWord is not in wordList, return 0.
  2. Queue beginWord with level = 1; keep a visited array over wordList.
  3. For each level, pop every word; for each unvisited list word that differs by one letter: if it equals endWord return level + 1, else mark and enqueue it.
  4. Increment level. If the queue empties, return 0.

Visualization

level 1: [hit]
level 2: [hot]           (hot differs from hit at index 1)
level 3: [dot, lot]
level 4: [dog, log]
level 5: cog found from dog -> return 5

Code

java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (!wordList.contains(endWord)) {
            return 0;
        }
        int n = wordList.size();
        boolean[] visited = new boolean[n];
        Deque<String> queue = new ArrayDeque<>();
        queue.offer(beginWord);
        int level = 1;

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int s = 0; s < size; s++) {
                String word = queue.poll();
                for (int i = 0; i < n; i++) {
                    if (visited[i] || !oneApart(word, wordList.get(i))) {
                        continue;
                    }
                    if (wordList.get(i).equals(endWord)) {
                        return level + 1;
                    }
                    visited[i] = true;
                    queue.offer(wordList.get(i));
                }
            }
            level++;
        }
        return 0;
    }

    // true if a and b differ in exactly one position
    private boolean oneApart(String a, String b) {
        int diff = 0;
        for (int i = 0; i < a.length(); i++) {
            if (a.charAt(i) != b.charAt(i) && ++diff > 1) {
                return false;
            }
        }
        return diff == 1;
    }
}

Complexity

  • Time: O(N² · L) — each of the N words may be compared against all N words, each comparison O(L).
  • Space: O(N) — queue and visited array.

Complexity Comparison

Approach Time Space
BFS Comparing All Words O(N² · L) O(N)
BFS with Letter Substitution O(N · L² · 26) O(N · L)
Bidirectional BFS O(N · L² · 26), much smaller in practice O(N · L)

Common Pitfalls

  • Returning the number of transformations instead of the number of words — the answer counts beginWord too.
  • Forgetting the early return 0 when endWord is not in wordList.
  • Marking words visited when they are popped instead of when they are enqueued, which lets the same word enter the queue many times.
  • Forgetting to restore chars[i] after trying all 26 letters at a position.
  • Using DFS — it finds a path, not the shortest one.