Description
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 <= 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].
Example 1:
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"
Example 2:
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.
Constraints:
1 <= beginWord.length <= 5
endWord.length == beginWord.length
1 <= wordList.length <= 500
wordList[i].length == beginWord.length
beginWord, endWord, and wordList[i] consist of lowercase English letters.
beginWord != endWord
All the words in wordList are unique.
The sum of all shortest transformation sequences does not exceed 10^5.
Solution
TLE: bfs + backtracing. Similar to 127. Word Ladder, this time we will need to print out the paths. So I will call 127’s function first, to get the shortest path length, and then use dfs/back tracking to find the paths that has this length.
Code
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
word_list = set(wordList)
if endWord not in word_list:
return 0
queue = collections.deque([(endWord, 1)])
visited = set()
while queue:
cur_word, step = queue.popleft()
if cur_word in visited:
continue
visited.add(cur_word)
if cur_word == beginWord:
return step
for i in range(len(cur_word)):
for dz in range(26):
new_char = chr(ord('a') + dz)
if new_char == cur_word[i]:
continue
new_word = f'{cur_word[:i]}{new_char}{cur_word[i+1:]}'
if new_word in word_list or new_word == beginWord:
queue.append((new_word, step + 1))
return 0
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
ladder_length = self.ladderLength(beginWord, endWord, wordList)
if ladder_length == 0:
return []
res = []
word_list = set(wordList)
candidates = {}
for each_word in word_list | set([beginWord]):
candidates[each_word] = []
for i in range(len(each_word)):
for dz in range(26):
new_char = chr(ord('a') + dz)
if new_char == each_word[i]:
continue
new_word = f'{each_word[:i]}{new_char}{each_word[i+1:]}'
if new_word in word_list:
candidates[each_word].append(new_word)
def dfs(cur_word: str, cur_res: list) -> None:
if cur_word == endWord and len(cur_res) == ladder_length:
res.append(cur_res)
elif len(cur_res) > ladder_length:
return
else:
for candidate_word in candidates[cur_word]:
dfs(candidate_word, cur_res + [candidate_word])
dfs(beginWord, [beginWord])
return res