A transformation sequence from word beginWord
to word endWord
using a dictionary wordList
is a sequence of words beginWord -> s1 -> s2 -> ... -> sk
such that:
si
for 1 <= i <= k
is in wordList
. Note that beginWord
does not need to be in wordList
.sk == endWord
Given two words, beginWord
and endWord
, and a dictionary wordList
, return all the shortest transformation sequences from beginWord
to endWord
, or an empty list if no such sequence exists. Each sequence should be returned as a list of the words [beginWord, s1, s2, ..., sk]
.
For example:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
Explanation: There are 2 shortest transformation sequences: "hit" -> "hot" -> "dot" -> "dog" -> "cog" "hit" -> "hot" -> "lot" -> "log" -> "cog"
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] Output: []
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.
from collections import defaultdict, deque
def findLadders(beginWord: str, endWord: str, wordList: list[str]) -> list[list[str]]:
# Convert wordList to a set for faster lookup
wordList = set(wordList)
# If the endWord is not in the wordList, there is no solution
if endWord not in wordList:
return []
# Build adjacency list (graph)
adj = defaultdict(list)
all_words = wordList | {beginWord}
for word1 in all_words:
for word2 in all_words:
if word1 != word2 and differ_by_one_letter(word1, word2):
adj[word1].append(word2)
# BFS to find shortest paths
queue = deque([(beginWord, [beginWord])])
visited = {beginWord}
shortest_len = float('inf')
result = []
while queue:
word, path = queue.popleft()
if len(path) > shortest_len:
break # Optimization: paths longer than shortest are useless
if word == endWord:
shortest_len = len(path)
result.append(path)
continue
for neighbor in adj[word]:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, path + [neighbor]))
# Filter out paths that are longer than the shortest path
final_result = []
for path in result:
if len(path) == shortest_len:
final_result.append(path)
return final_result
def differ_by_one_letter(word1: str, word2: str) -> bool:
diff_count = 0
for i in range(len(word1)):
if word1[i] != word2[i]:
diff_count += 1
return diff_count == 1
# Example Usage
beginWord = "hit"
endWord = "cog"
wordList = ["hot", "dot", "dog", "lot", "log", "cog"]
print(findLadders(beginWord, endWord, wordList))
beginWord = "hit"
endWord = "cog"
wordList = ["hot", "dot", "dog", "lot", "log"]
print(findLadders(beginWord, endWord, wordList))
findLadders(beginWord, endWord, wordList)
function:
beginWord
, endWord
, and wordList
as input.wordList
to a set for efficient lookup.endWord
is not in wordList
by returning an empty list.beginWord
to endWord
.
visited
set to avoid cycles.differ_by_one_letter(word1, word2)
function:
True
if they differ by one letter, False
otherwise.endWord
not in wordList
:
endWord
is not present in wordList
.No transformation sequence exists:
beginWord
to endWord
, the BFS will terminate without finding any paths, and an empty list will be returned.beginWord
and endWord
are the same:
Empty wordList
:
wordList
is empty and the beginWord
and endWord
are different, it will return an empty list.Large wordList
:
wordList
due to the time complexity of building the adjacency list (O(N^2 * L)). Optimizations like pre-computing neighbors for each word can be employed, if required, in a production environment.