[Leetcode] 127 Word Ladder 解题

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...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值