class Solution:
#################################################
###### solution 3: Bi-directional BFS ######
# search from both side and check if they meet #
#################################################
from collections import deque
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if endWord not in wordList:
return 0
begin_queue = collections.deque([beginWord])
# key = word, value = the path from beginWord to this word
begin_visited = {beginWord: 0}
end_queue = collections.deque([endWord])
# key = word, value = the path from endWord to this word
end_visited = {endWord: 0}
while begin_queue and end_queue:
# 1.search from the smaller queue as an optimization
shorter_queue, longer_queue = self._find_shorter_and_longer_queue(begin_queue, end_queue)
if len(begin_queue) <= len(end_queue):
shorter_visited, longer_visited = begin_visited, end_visited
else:
shorter_visited, longer_visited = end_visited, begin_visited
# 2. check if word is in both shorter_visited and longer_visited
word = shorter_queue.popleft()
if word in longer_visited:
# !! this word shows up in both directions, we find it
# !! 1 + distance from both directions
return 1 + shorter_visited[word] + longer_visited[word]
# 3. prepare for the next level
for next_word in self._get_next_words(word, wordList):
if next_word in shorter_visited:
continue
shorter_queue.append(next_word)
shorter_visited[next_word] = shorter_visited[word] + 1
# no ladder found!
return 0
def _find_shorter_and_longer_queue(self, queue_1: deque[str], queue_2: deque[str] ) -> (deque[str], deque[str]):
if len(queue_1) <= len(queue_2):
return queue_1, queue_2
else:
return queue_2, queue_1
#################################################
# solution 2: single-direction BFS ########
# visited = {word: 0} remembers the visited and length
# V.S
# visited = {word} # in solution 1 ######
#################################################
def ladderLength_bfs(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if endWord not in wordList:
return 0
queue = collections.deque([beginWord])
distances = {beginWord: 0}
while queue:
word = queue.popleft()
distance = distances[word] + 1
# !! found end word
if word == endWord:
return distance
# prepare for next level
for next_word in self._get_next_words(word, wordList):
if next_word in distances:
continue
queue.append(next_word)
distances[next_word] = distance
# no ladder found
return 0
#################################################
#### solution 1 ##################
#################################################
def ladderLength_level_by_level(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
# if end word not in wordList, then
# no way we can reach it
if endWord not in wordList:
return 0
# BFS to find shortest path in graph
# 1. initialize
# !! add the end word in dict because it's not there yet
queue = collections.deque([beginWord])
visited = set([beginWord])
distance = 0
while queue:
distance += 1
for _ in range(len(queue)):
word = queue.popleft()
# !! found end word
if word == endWord:
return distance
# prepare for next level
for next_word in self._get_next_words(word, wordList):
if next_word in visited:
continue
queue.append(next_word)
visited.add(next_word)
# no ladder found!
return 0
# get a set of words that can be reached from word
def _get_next_words(self, word: str, wordList: List[str]) -> set[str]:
# try to change 1 character in word and
# check if the new_word exists in wordList
word_dict = set(wordList)
next_words = set()
for i in range(len(word)):
ch = word[i]
left_substr = word[ : i]
right_substr = word[i + 1 : ]
for letter in "abcdefghijklmnopqrstuvwxyz":
if letter == ch:
# skip if the word is itself
continue
new_word = left_substr + letter + right_substr
if new_word in word_dict:
next_words.add(new_word)
# print('next for ', word, next_words)
return next_words
经典的用BFS解决 shortest path问题,加分项是Bi-directional BFS
Loading...