Lesson 19 of 47 3 min

MANG Problem #17: Word Ladder (Hard)

Learn how to find the shortest transformation sequence between two words using Breadth-First Search on a string graph.

1. Problem Statement

A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:

  • Every adjacent pair of words differs by a single letter.
  • Every si for $1 \le i \le k$ is in wordList.

Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence, or 0 if no such sequence exists.

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5 (hit -> hot -> dot -> dog -> cog)

2. Approach: Breadth-First Search (BFS)

Shortest path in an unweighted graph is always a BFS problem.

  1. Nodes: Every word in the list.
  2. Edges: Two words have an edge if they differ by exactly one letter.
  3. Traversal: Start from beginWord, explore all neighbors (1-letter diff), then their neighbors, and so on. The first time we hit endWord, the current level is our shortest path.

3. Java Implementation

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    Set<String> set = new HashSet<>(wordList);
    if (!set.contains(endWord)) return 0;
    
    Queue<String> queue = new LinkedList<>();
    queue.add(beginWord);
    
    int level = 1;
    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            String curr = queue.poll();
            if (curr.equals(endWord)) return level;
            
            // Try changing every character (a to z)
            char[] words = curr.toCharArray();
            for (int j = 0; j < words.length; j++) {
                char originalChar = words[j];
                for (char c = 'a'; c <= 'z'; c++) {
                    if (c == originalChar) continue;
                    words[j] = c;
                    String target = String.valueOf(words);
                    if (set.contains(target)) {
                        queue.offer(target);
                        set.remove(target); // Mark as visited
                    }
                }
                words[j] = originalChar;
            }
        }
        level++;
    }
    return 0;
}

4. 5-Minute "Video-Style" Walkthrough

  1. The "Aha!" Moment: Imagine a massive cloud of words. A "transformation" is like jumping from one word to another. We want the fewest jumps. BFS ensures we explore all 1-jump words before any 2-jump words.
  2. The Neighbor Search: To find neighbors of "hit", don't compare it to every word in the list ($O(N \times L)$). Instead, change every letter of "hit" ($O(26 \times L)$) and check if that new string is in our set. This is much faster when the dictionary is large.
  3. The Efficiency: Removing words from the set after adding them to the queue is our "visited" logic. It prevents us from jumping in circles.

5. Interview Discussion

  • Interviewer: "What is the time complexity?"
  • You: "O(N * L^2) where N is the number of words and L is the length. We iterate through N words, and for each, we do L length loops with string generation."
  • Interviewer: "Can we optimize this?"
  • You: "Yes, with Bidirectional BFS. By starting the search from both beginWord and endWord and meeting in the middle, we significantly reduce the search space (the exponent of the branching factor is halved)."

Want to track your progress?

Sign in to save your progress, track completed lessons, and pick up where you left off.