Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

127. Word Ladder - Leetcode Solution

Code Implementation

from collections import deque

class Solution:
    def ladderLength(self, beginWord, endWord, wordList):
        wordSet = set(wordList)
        if endWord not in wordSet:
            return 0

        queue = deque()
        queue.append((beginWord, 1))
        visited = set()
        visited.add(beginWord)
        
        while queue:
            current_word, steps = queue.popleft()
            if current_word == endWord:
                return steps
            for i in range(len(current_word)):
                for c in 'abcdefghijklmnopqrstuvwxyz':
                    next_word = current_word[:i] + c + current_word[i+1:]
                    if next_word in wordSet and next_word not in visited:
                        visited.add(next_word)
                        queue.append((next_word, steps + 1))
        return 0
      
#include <string>
#include <vector>
#include <unordered_set>
#include <queue>
using namespace std;

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        if (wordSet.find(endWord) == wordSet.end()) return 0;

        queue<pair<string, int>> q;
        q.push({beginWord, 1});
        unordered_set<string> visited;
        visited.insert(beginWord);

        while (!q.empty()) {
            auto [current, steps] = q.front();
            q.pop();
            if (current == endWord) return steps;
            for (int i = 0; i < current.size(); ++i) {
                string temp = current;
                for (char c = 'a'; c <= 'z'; ++c) {
                    temp[i] = c;
                    if (wordSet.count(temp) && !visited.count(temp)) {
                        visited.insert(temp);
                        q.push({temp, steps + 1});
                    }
                }
            }
        }
        return 0;
    }
};
      
import java.util.*;

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Set<String> wordSet = new HashSet<>(wordList);
        if (!wordSet.contains(endWord)) return 0;

        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        Set<String> visited = new HashSet<>();
        visited.add(beginWord);
        int steps = 1;

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String word = queue.poll();
                if (word.equals(endWord)) return steps;
                char[] chars = word.toCharArray();
                for (int j = 0; j < chars.length; j++) {
                    char original = chars[j];
                    for (char c = 'a'; c <= 'z'; c++) {
                        chars[j] = c;
                        String next = new String(chars);
                        if (wordSet.contains(next) && !visited.contains(next)) {
                            visited.add(next);
                            queue.offer(next);
                        }
                    }
                    chars[j] = original;
                }
            }
            steps++;
        }
        return 0;
    }
}
      
/**
 * @param {string} beginWord
 * @param {string} endWord
 * @param {string[]} wordList
 * @return {number}
 */
var ladderLength = function(beginWord, endWord, wordList) {
    const wordSet = new Set(wordList);
    if (!wordSet.has(endWord)) return 0;

    const queue = [];
    queue.push([beginWord, 1]);
    const visited = new Set();
    visited.add(beginWord);

    while (queue.length) {
        const [current, steps] = queue.shift();
        if (current === endWord) return steps;
        for (let i = 0; i < current.length; i++) {
            for (let c = 97; c <= 122; c++) {
                const next = current.slice(0, i) + String.fromCharCode(c) + current.slice(i + 1);
                if (wordSet.has(next) && !visited.has(next)) {
                    visited.add(next);
                    queue.push([next, steps + 1]);
                }
            }
        }
    }
    return 0;
};
      

Problem Description

You are given three inputs: a beginWord, an endWord, and a wordList (an array of valid words). Your task is to transform beginWord into endWord by changing exactly one letter at a time. Each transformed word must exist in wordList, and you may not reuse the same word more than once in a transformation sequence.

Return the minimum number of transformations (including the beginWord and endWord) needed to reach endWord from beginWord. If there is no such transformation sequence, return 0.

  • Only one letter can be changed at a time.
  • Each transformed word must exist in wordList.
  • No word may be reused in a sequence.
  • There is exactly one valid shortest transformation length if a path exists.

Thought Process

At first glance, the problem seems to require trying all possible sequences of word transformations, but this quickly becomes infeasible as the size of wordList grows. A brute-force approach would involve generating all possible transformation paths and checking each for validity, but this leads to an exponential number of possibilities.

To optimize, we can recognize this problem as a shortest path search in an unweighted graph: each word is a node, and an edge exists between two words if they differ by exactly one letter. The task is to find the shortest path from beginWord to endWord in this graph.

Since we are looking for the shortest transformation, Breadth-First Search (BFS) is the natural choice. BFS explores all neighbors at the current depth before moving deeper, ensuring that the first time we reach the endWord is via the shortest sequence.

Solution Approach

We will use BFS to find the shortest transformation sequence. Here's a step-by-step breakdown:

  1. Convert wordList to a set: For O(1) lookup time, store all words in a set called wordSet. If endWord is not in wordSet, immediately return 0.
  2. Initialize a queue for BFS: Each queue element stores the current word and the number of steps taken so far. Start by enqueuing (beginWord, 1).
  3. Track visited words: Use a set to ensure we don't revisit the same word.
  4. BFS Loop:
    • Dequeue the current word and its step count.
    • If the current word matches endWord, return the step count.
    • For each character position in the current word, try replacing it with every letter from 'a' to 'z' to generate all possible one-letter transformations.
    • If a generated word exists in wordSet and hasn't been visited, add it to the queue and mark it as visited.
  5. No Path Found: If the queue is exhausted without finding endWord, return 0.

The BFS ensures the shortest path is found because it explores all words at a given transformation distance before moving deeper.

Example Walkthrough

Let's walk through an example:

  • beginWord = "hit"
  • endWord = "cog"
  • wordList = ["hot","dot","dog","lot","log","cog"]

Step 1: Start with "hit", steps = 1.
Step 2: Possible one-letter transformations: "hot" (in wordList, add to queue).
Step 3: Next, "hot", steps = 2.
Step 4: Transformations: "dot" and "lot" (both in wordList, add to queue).
Step 5: Next, "dot", steps = 3.
Step 6: Transformations: "dog" (in wordList, add to queue).
Step 7: Next, "lot", steps = 3.
Step 8: Transformations: "log" (in wordList, add to queue).
Step 9: Next, "dog", steps = 4.
Step 10: Transformations: "cog" (matches endWord, return steps + 1 = 5).

The shortest transformation sequence is: "hit" → "hot" → "dot" → "dog" → "cog", which is 5 steps.

Time and Space Complexity

Brute-force approach: Would generate all possible transformation sequences, leading to exponential time and space complexity, specifically O(N!) where N is the number of words.

Optimized BFS approach:

  • Time Complexity: O(M2 * N), where M is the length of each word and N is the number of words in wordList. For each word, we try changing each letter (M positions), and for each position, 26 possible letters, but since we only enqueue valid words, it's bounded by total words.
  • Space Complexity: O(M * N) for the queue, visited set, and word set.

The BFS ensures we do not revisit words, keeping the process efficient.

Summary

The Word Ladder problem is elegantly solved by modeling it as a shortest path problem in a graph where words are nodes and edges connect one-letter transformations. Using BFS, we guarantee the shortest transformation sequence is found efficiently. Key insights include using a set for fast lookups and a queue for BFS traversal, ensuring each word is visited at most once. This approach scales well and avoids the pitfalls of brute-force enumeration.