Solving Leetcode Interviews in Seconds with AI: Word Ladder II
Introduction
In this blog post, we will explore how to solve the LeetCode problem "126" using AI. LeetCode is a popular platform for preparing for coding interviews, and with the help of AI tools like Chatmagic, we can generate solutions quickly and efficiently - helping you pass the interviews and get the job offer without having to study for months.
Problem Statement
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 105.
Explanation
Here's a breakdown of the approach, complexities, and the Python code for finding all shortest transformation sequences:
- Breadth-First Search (BFS): Use BFS to explore the possible word transformations level by level. This guarantees finding the shortest paths first.
- Graph Representation (Implicit): We don't explicitly build a graph. Instead, we generate neighbors (words one letter apart) on the fly from the
wordListduring the BFS. Path Tracking: Maintain a dictionary to store the predecessors of each word in the paths we discover. This allows us to reconstruct all shortest paths once we reach the
endWord.Runtime Complexity: O(V + E), where V is the number of words in the word list, and E is the number of possible transitions between words (which can be approximated by V 26 L, where L is the word length). Overall, close to O(N*M) where N is the number of words in wordList, and M is the length of a word. Storage complexity: O(V + P), where P is the total length of all shortest paths combined (which can be O(V^2) in worst case scenarios but according to constraints it does not exceed 10^5).
Code
from collections import defaultdict, deque
def findLadders(beginWord: str, endWord: str, wordList: list[str]) -> list[list[str]]:
"""
Finds all shortest transformation sequences from beginWord to endWord using words from wordList.
Args:
beginWord: The starting word.
endWord: The target word.
wordList: A list of words to use in the transformation.
Returns:
A list of lists, where each inner list is a shortest transformation sequence.
Returns an empty list if no such sequence exists.
"""
if endWord not in wordList:
return []
wordList = set(wordList) # For faster lookup
queue = deque([(beginWord, [beginWord])])
visited = {beginWord}
all_paths = []
min_length = float('inf')
while queue:
word, path = queue.popleft()
if len(path) > min_length:
break # Optimization: Stop if current path exceeds min length
if word == endWord:
min_length = len(path)
all_paths.append(path)
continue
for i in range(len(word)):
for char_code in range(ord('a'), ord('z') + 1):
new_char = chr(char_code)
new_word = word[:i] + new_char + word[i + 1:]
if new_word in wordList and new_word not in visited:
new_path = path + [new_word]
queue.append((new_word, new_path))
visited.add(new_word)
#Filter out longer paths after BFS
filtered_paths = [path for path in all_paths if len(path) == min_length]
return filtered_paths