Given two words, beginWord
and endWord
, and a dictionary wordList
, determine the shortest transformation sequence from beginWord
to endWord
. The transformation must adhere to these rules:
wordList
(except for the beginWord
).Return the number of words in the shortest transformation sequence, or 0 if no such sequence exists.
For example:
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.
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.
Write a function to efficiently solve this problem.
A brute-force approach would involve exploring all possible transformation sequences from the beginWord
to the endWord
. This could be done using a graph-like traversal, where each word in the wordList
is a node, and an edge exists between two words if they differ by only one letter.
wordList
. If two words differ by only one letter, add an edge between them in the graph.beginWord
, perform a Depth-First Search (DFS) or Breadth-First Search (BFS) to find all possible paths to endWord
.endWord
is reachable, determine the shortest path among all discovered paths.Problems with this approach:
wordList
sizes, because it explores many unnecessary paths.Big O Runtime: O(V + E) where V is the number of words in the dictionary and E is the number of edges between words.
Big O Space: O(V + E) due to the storage of the graph.
A more efficient approach uses Breadth-First Search (BFS) to find the shortest transformation sequence. BFS guarantees finding the shortest path in an unweighted graph.
Algorithm:
beginWord
.visited
set to keep track of visited words to avoid cycles.distance
map to store the distance (number of transformations) from beginWord
to each word. Set distance[beginWord] = 1
.currentWord
.currentWord
is equal to endWord
, return distance[currentWord]
.currentWord
.wordList
and has not been visited:
distance[variation] = distance[currentWord] + 1
.endWord
is not reached, return 0.Big O Runtime: O(M^2 * N), where N is the number of words in wordList
and M is the length of each word.
Big O Space: O(M * N), where N is the number of words in wordList
and M is the length of each word.
Edge Cases:
endWord
is not in wordList
, there is no transformation sequence, return 0.beginWord
and endWord
are the same, return 0 if the wordList doesn't have the beginWord
or 1 if the wordList
contains the beginWord
.wordList
is empty, return 0.from collections import deque
def ladderLength(beginWord: str, endWord: str, wordList: list[str]) -> int:
if endWord not in wordList:
return 0
wordSet = set(wordList)
queue = deque([(beginWord, 1)])
visited = {beginWord}
while queue:
word, length = queue.popleft()
if word == endWord:
return length
for i in range(len(word)):
for char_code in range(ord('a'), ord('z') + 1):
char = chr(char_code)
new_word = word[:i] + char + word[i+1:]
if new_word in wordSet and new_word not in visited:
queue.append((new_word, length + 1))
visited.add(new_word)
return 0