# 127. Word Ladder #### Difficulty: Hard link: https://leetcode.com/problems/word-ladder/ 找shortest path用BFS。 ### 1. BFS 這題很多鳥鳥的地方,也是面試的時候需要先與面試官澄清的點。 第一點,beginWord和endWord都可能不在wordList,但題目只提醒beginWord。 第二點,如果建graph的時候,直接檢查wordList裡的每個字是不是鄰居,由於wordList的長度最大是5000,這樣會TLE。 改為用26個英文字母,先生成可能的鄰居word,再檢查是否存在於wordList,Word長度最大是10乘以26字母等於260,就不會TLE。 ##### python ```python= class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: wordList = set(wordList) if endWord not in wordList: return 0 wordList.add(beginWord) graph = {} for word in wordList: graph[word] = [] for i in range(len(word)): for c in 'abcdefghijklmnopqrstuvwxyz': next_word = word[:i] + c + word[i+1:] if next_word in wordList: graph[word].append(next_word) length = 1 visited = {beginWord} queue = deque([beginWord]) while queue: length += 1 for _ in range(len(queue)): node = queue.popleft() for neigh in graph[node]: if neigh == endWord: return length if neigh not in visited: visited.add(neigh) queue.append(neigh) return 0 ``` 不建graph也行,traverse時再找鄰居有哪些word。 ```python= class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: wordList = set(wordList) if endWord not in wordList: return 0 queue = collections.deque([[beginWord, 1]]) while queue: word, length = queue.popleft() if word == endWord: return length for i in range(len(word)): for c in 'abcdefghijklmnopqrstuvwxyz': next_word = word[:i] + c + word[i+1:] if next_word in wordList: wordList.remove(next_word) queue.append([next_word, length + 1]) return 0 ``` ### 2. Bidirectional BFS 進一步優化,不知道從beginWord和endWord哪一邊開始做BFS比較快。 其實,可以兩邊同時做BFS,每一輪都檢查哪一邊的queue長度比較短,就從那邊繼續做BFS,整體來說會比較快。 把wordList當作non visited set,節省空間。 ```python= class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: wordList = set(wordList) if endWord not in wordList: return 0 # remove visited word in wordList to prevent BFS from visiting the same word wordList.discard(beginWord) wordList.discard(endWord) length = 1 begin_set, end_set = {beginWord}, {endWord} while begin_set and end_set: length += 1 next_set = set() for word in begin_set: for i in range(len(word)): for c in 'abcdefghijklmnopqrstuvwxyz': next_word = word[:i] + c + word[i+1:] if next_word in end_set: return length if next_word in wordList: next_set.add(next_word) wordList.discard(next_word) begin_set = next_set if len(begin_set) > len(end_set): begin_set, end_set = end_set, begin_set return 0 ``` ###### tags: `leetcode`