leecode:单词接龙之二(python)

1. 题目描述

给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:

每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。

说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:
[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]
示例 2:
输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出: []
解释: endWord “cog” 不在字典中,所以不存在符合要求的转换序列。

2. 思路

想了三天,参考了一下别人的思路,试了好几遍,终于AC了。
每一个代码的具体目的都在代码里作了注释。

这一题是单词接龙的进阶版,要返回所有的转换序列。这里借鉴了南郭子綦的思路。
以示例1为例:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
具体来说,主要做了以下几部分的工作:
在这里插入图片描述

  1. 用字典建立beginWord和endWord之间单词的联系,其中字典的key是后面的单词,val是key前面的单词所构成的单词数组。如:hot的前一个单词是hit。当key为beginWord时,val为[],这个例子最终建立的字典是memories = {“cog”:[“log”,“dog”], “log”:[“lot”], “dog”:[“dot”], “dot”:[“hot”], “lot”:[“hot”], “hot”:[“hit”],“hit”:[]}。
  2. 根据构建好的字典,利用带有回溯的dfs方法生成从beginWord到endWord的转化路径。
  3. 在构建字典的时候,我们可以利用层次遍历的方法,借助两个集合,preset和curset。其中preset集合里是前一层的单词,curset表示当前层的单词集合。即curset中的单词是key,preset里的单词是value,当开始循环的时候,这一次循环的curset是下一层循环的preset,为了避免重复,每循环完一次,都要把curset里的单词从wordList中去掉(或者循环之前把preset里的单词去掉)。

2.1 python代码

class Solution:
    def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
        # 利用dfs构建路径
        def buildPath(path,word):   
        # dfs从字典中构建出beginWord到endWord的路径,其中这个路径生成函数是从endWord往前倒推的
            if len(memories[word]) == 0:  
            # 因为如果字典构建完成的话,在一整条完整的路径中,
            # 只有beginWord的val是[],其他的都至少有一个单词,
            # 否则连不成一个完整的路径。
            # 这是需要将word添加进path头部(因为是倒推的)然后压入结果中
                res.append([word] + path)  
                return
            # 如果不是beginWord,将其插入path头部,然后进入这个单词的上一层单词。
            # 因为可能有多个单词变换一个字母得到当前单词,利用一个for循环,
            # 从字典中读取这个单词的上一层单词,分别进行递归。
            path.insert(0,word)  
            for element in memories[word]:
                buildPath(path,element)
        # 由于路径可能不止一条(某个单词的上层单词有多个,或者某个单词的下层单词有多个)  
        # 因此需要将插入头部的元素拿出来。(回溯),每运行(递归)一次,就拿出一个元素。
        # 令path恢复到for循环之前的path。
            path.pop(0)
        # 二: 构建字典
        memories = {} 
        memories[beginWord] = []  # 将beginWord的val值设置成[]
        wordList = set(wordList)  # 因为不会重复,先对wordList进行降重。
        wordList.discard(beginWord)  # 将beginWord从wordList中删除
        # 先初始化memories,使其key为wordList中的单词,val统一先设置成[]
        for i in wordList:  
            memories[i] = []
        # 借助两个集合(preset和curset)存储上一层单词和目前层的单词
        curset = set()
        curset.add(beginWord)
        preset = set()
        lenth = len(beginWord)
        res = []
        while True:
            preset = curset   # 上一次循环的curset变成这一次循环的precut。
            # 由于curset是存储这一次循环的当前层元素的,因此每次循环前,
            # 都要将curset重新清空,以便填充当前层元素
            curset = set()    
   			# 对在上一层中的单词进行判断,看是否有还在wordList中的单词,
   			# 可以由上一层单词一次变化得到。有的话,就修改字典中的相应key的val值。
   			# 同时将这个单词压入curset(当前层)
            for preword in preset:
                for i in range(lenth):
                    left = preword[:i]
                    right = preword[i+1:]
                    for byte in "abcdefghijklmnopqrstuvwxyz":
                        if byte != preword[i]:
                            tempword = left + byte + right
                            if tempword in wordList:
                                memories[tempword].append(preword)
                                curset.add(tempword)
            # 为了避免重复,每检查完一次,都需要将当前层单词从wordList中删除,
            # 要不然下一次循环的上一层元素,不能出现重复使用的情况。
            for word in curset:
                wordList.discard(word)
            # 当构建字典的过程中,如果上一层的所有元素都无法通过一次変换
            # 得到wordList中的单词),说明这个路径就会打断,即找不到从begin到end的路径
            # 不用再检查,直接返回[]。当然在字典中可能会有其他的单词的val为空,
            # 只要不在路径上就不影响。(不在在precut里)
            if len(curset) == 0:
                return []
            # 如果endWord在curset中,即endWord是当前循环的当前层单词,
            # 说明已经到达endWord了,打断循环即可。endWord不会是任何单词的上一层元素。
            if endWord in curset:
                break
        buildPath([],endWord)  #将字典构建成路径结果返回
        return res

猜你喜欢

转载自blog.csdn.net/ggdhs/article/details/91645870