Problem
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
for1 <= i <= k
is inwordList
. Note thatbeginWord
does not need to be inwordList
. sk == endWord
Given two words, beginWord
and endWord
, and a dictionary wordList
, return the number of words in the shortest transformation sequence from beginWord
to endWord
, or 0
if no such sequence exists.
OR
Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
- only one letter can be changed at a time and
- each intermediate word must exist in the dictionary.
Examples
Example 1:
Input:
beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output:
5
Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.
Example 2:
Input:
beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output:
0
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.
Note that we account for the length of the transformation path instead of the number of transformation itself.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
Solution
Method 1 - BFS
To find the shortest transformation sequence, we can perform a Breadth-First Search (BFS). BFS is suitable here as it explores all possible transformations level by level, ensuring that the first time we reach the endWord
, we have found the shortest path.
Approach
- Initial Checks: Ensure
endWord
is in thewordList
. If not, return 0. - BFS Initialization: Use a queue to store the current word and its transformation depth. Begin with the
beginWord
and a depth of 1. - BFS Execution:
- For each word, generate all possible single-letter transformations.
- For each transformation, if it matches the
endWord
, return the current depth + 1. - If the transformation is in
wordList
, add it to the queue and remove it fromwordList
to prevent revisiting.
- Return: If the queue is exhausted without finding the
endWord
, return 0.
graph TD; A["hit"] --> B["a̲it"]:::nonAns & C["b̲it"]:::nonAns & D["..."]:::nonAns & E["z̲it"]:::nonAns & F["ha̲t"]:::nonAns & G["..."]:::nonAns & H["ho̲t"]:::ans & I["..."]:::nonAns & J["hz̲t"]:::nonAns & K["..."]:::nonAns & L["hiz̲"]:::nonAns H ---> P["..."]:::nonAns & Q["d̲ot"]:::ans & R["..."]:::nonAns classDef ans fill:#fc6b03 classDef nonAns stroke-width:2px, stroke-dasharray: 5 5, font-size:10pt
Code
Java
public 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);
int ans = 1;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
String word = queue.poll();
if (word.equals(endWord)) return ans;
char[] chars = word.toCharArray();
for (int j = 0; j < chars.length; j++) {
char originalChar = chars[j];
for (char c = 'a'; c <= 'z'; c++) {
if (chars[j] == c) continue;
chars[j] = c;
String newWord = new String(chars);
if (wordSet.contains(newWord)) {
queue.offer(newWord);
wordSet.remove(newWord);
}
}
chars[j] = originalChar;
}
}
ans++;
}
return 0;
}
}
Python
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
wordSet: Set[str] = set(wordList)
if endWord not in wordSet:
return 0
queue = deque([beginWord])
ans: int = 1
while queue:
size = len(queue)
for _ in range(size):
word = queue.popleft()
if word == endWord:
return ans
for i in range(len(word)):
original_char = word[i]
for c in range(26):
new_char = chr(ord('a') + c)
if word[i] == new_char:
continue
new_word = word[:i] + new_char + word[i+1:]
if new_word in wordSet:
queue.append(new_word)
wordSet.remove(new_word)
ans += 1
return 0
Complexity
- ⏰ Time complexity:
O(M*N)
whereM
is the length of each word andN
is the total number of words in thewordList
, because generating all possible transformations and comparing them takes linear time. - 🧺 Space complexity:
O(M*N)
due to the space needed for the queue and the intermediate transformations.