LeetCode Word Ladder II

LeetCode解題之Word Ladder II


原題

給定一個起始字符串和一個目標字符串,現在將起始字符串按照特定的變換規則轉換爲目標字符串,求所有轉換次數最少的轉換過程。轉換規則爲每次只能改變字符串中的一個字符,且每次轉換後的字符串都要在給定的字符串集合中。

注意點:

  • 所有給出的字符串的長度都相等
  • 所有的字符都爲小寫字母

例子:

輸入: beginWord = “hit”, endWord = “cog”, wordList = [“hot”,”dot”,”dog”,”lot”,”log”]

輸出:

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

解題思路

整體思想與Word Ladder一樣,都是採用DFS的方法。不過要做出以下的變化:

  1. 在Word Ladder中,廣度優先遍歷是從上層到下層一層層遍歷下去的,還是以下面的圖爲例,樹結構可能是中間寬兩頭窄的情況。而一個節點轉換爲下層節點時,要依次將它字符串中的每個字符用其他字母替換後與給定字符串集合進行比較。如果樹某一層的節點非常多,那麼這個嘗試轉換的操作開銷就很大。還需要明確的一點是,題目中的轉換是可逆的,A可以轉換爲B,那麼B也可以轉換爲A,且A和B都能轉換爲它們轉換過程的某一個狀態C。綜上所述,我們可以從起始字符串和目標字符串同時進行轉換,哪一端的節點數目少,我們就選這些節點繼續進行轉換,直到它們匯合到同一個節點或者轉換終止(也就是下一層沒有節點)。代碼中由變量is_forward來表示從哪一端轉換。
  2. 現在是要求所有最少轉換次數的轉換方法,所以要將所有的轉換可能都找出來。如圖中bit和him轉換爲bim的轉換關係我們都要找出來。
  3. 我們還需要記錄轉換的路徑,我們將從上一層到下一層的轉換關係記錄下來,等到確定能夠轉換成功了,再通過深度優先遍歷的方法將轉換路徑組裝起來。

word ladder

注:圖中的有些單詞沒有意義,只是單純爲了舉例子,圖對應的起始字符串爲hit,給定的字符串集合爲{“hot”,”hat”,”bit”,”him”,”bot”,”bim”}

AC源碼

class Solution(object):
    def findLadders(self, beginWord, endWord, wordlist):
        """
        :type beginWord: str
        :type endWord: str
        :type wordlist: Set[str]
        :rtype: List[List[int]]
        """

        def bfs(front_level, end_level, is_forward, word_set, path_dic):
            if len(front_level) == 0:
                return False
            if len(front_level) > len(end_level):
                return bfs(end_level, front_level, not is_forward, word_set, path_dic)
            for word in (front_level | end_level):
                word_set.discard(word)
            next_level = set()
            done = False
            while front_level:
                word = front_level.pop()
                for c in 'abcdefghijklmnopqrstuvwxyz':
                    for i in range(len(word)):
                        new_word = word[:i] + c + word[i + 1:]
                        if new_word in end_level:
                            done = True
                            add_path(word, new_word, is_forward, path_dic)
                        else:
                            if new_word in word_set:
                                next_level.add(new_word)
                                add_path(word, new_word, is_forward, path_dic)
            return done or bfs(next_level, end_level, is_forward, word_set, path_dic)

        def add_path(word, new_word, is_forward, path_dic):
            if is_forward:
                path_dic[word] = path_dic.get(word, []) + [new_word]
            else:
                path_dic[new_word] = path_dic.get(new_word, []) + [word]

        def construct_path(word, end_word, path_dic, path, paths):
            if word == end_word:
                paths.append(path)
                return
            if word in path_dic:
                for item in path_dic[word]:
                    construct_path(item, end_word, path_dic, path + [item], paths)

        front_level, end_level = {beginWord}, {endWord}
        path_dic = {}
        bfs(front_level, end_level, True, wordlist, path_dic)
        path, paths = [beginWord], []
        construct_path(beginWord, endWord, path_dic, path, paths)
        return paths


if __name__ == "__main__":
    assert Solution().findLadders("hit", "cog", {"hot", "dot", "dog", "lot", "log"}) == [
        ["hit", "hot", "dot", "dog", "cog"],
        ["hit", "hot", "lot", "log", "cog"]
    ]

歡迎查看我的Github (https://github.com/gavinfish/LeetCode-Python) 來獲得相關源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章