算法设计与分析:Word Ladder(Week 4)

学号:16340008

题目:127. Word Ladder


Question:

Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

  1. Only one letter can be changed at a time.
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Note:

  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

Example 1:

Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output: 5

Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.

Example 2:

Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: 0

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

Answer:

题目的意思如下:题目给出一个单词beginWord,一个单词列表wordList,一个单词endWord,要求我们从beginWord开始,每次只能变单词中的一个字母,使其成为wordList中的一个单词,重复若干次上述步骤直到变化成endWord,求最少的变化次数(若能成功变化成endWord)。

分析题目我们可以得到以下方案:

  1. 对beginWord,以wordList作为候选池,进行BFS,得到其下一层A
  2. 层A中的所有单词从候选池(wordList)中删去,避免搜索路径树出现环。而且其他将来可能经过该层单词的路径都将比层A发散出去的路径更长
  3. 将层A中的单词推进队列
  4. 队列头弹出一个单词,对其BFS,所有下一层的单词都从wordList中删去,并将该层单词推进队列。(从树根到队列头单词的下一层某单词K的步数肯定小于等于从树根到队列中其他单词到单词K的步数)
  5. 重复第四步,直到某队列头的下一层单词中有endWord(有解)或队列为空(无解)

在函数开始,先检测了endWord是否在wordList中,避免无谓的路径搜索。

声明了类State,有属性单词和属性步数,作为搜索的节点记录数据,相当于树的节点。方法中将State对象推进或弹出队列,对搜索到的下一层单词创建State对象记录其单词和步数(上一层+1)。

从wordList删除搜索到的下一层单词是为了后续搜索时能搜索更少的节点。

定义了方法check用于校验两个单词是否只差一个字母。

然后得到初步的代码如下(Python3):

import time
import queue
import copy

class State:
    def __init__(self, on, times):
        self.now = on
        self.times = times

class Solution:
    def ladderLength(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: int
        """
        self.q = queue.Queue()
        if endWord not in wordList:
            return 0
        
        self.list = wordList
        self.end = endWord
        begin = State(beginWord, 1)
        self.q.put(begin)
        
        while not self.q.empty():
            temp = self.q.get()
            i = 0
            while i < len(self.list):
                word = self.list[i]
                if self.check(word, temp.now):
                    if word == self.end:
                        return temp.times + 1
                    if word != temp.now:
                        self.q.put(State(word, temp.times+1))
                    self.list.remove(word)
                else: i += 1
        return 0


    def check(self, wordA, wordB):
        flag = True
        for i in range(len(wordA)):
            if wordA[i] != wordB[i] and flag == True:
                flag = False
            elif wordA[i] != wordB[i] and flag == False:
                return False
        return True

 然而当wordList中元素足够多(如以下代码中的29/39次test的2855个数据)时,提交反馈Time Limit Exceeded。这是因为搜索的效率低下。对于一个弹出的队列头单词A,搜索与他相似(指只相差一个字母)的单词需要遍历整个wordList,复杂度为O(n),同时remove一个相似单词需要对所有后续元素进行移动。若用一个标识(如0)取代remove,在另一方面由加重了遍历的工作量(实际上通过time计时发现时间更长了)。由于python3中list的数据结构相当于一个动态数组,因此假如不改变算法,这将无可避免。曾考虑使用链表,然而只能优化remove而无法改变搜索需要的遍历。

以下代码为测试代码,其中wordList有2855个数据,用以上算法在VScode调试下计时得6-7s,步数为11。

test = Solution()
beginWord = "sand"
endWord = "acne"
wordList = ["slit","bunk","wars","ping","viva","wynn","wows","irks","gang","pool","mock","fort","heel","send","ship","cols","alec","foal","nabs","gaze","giza","mays","dogs","karo","cums","jedi","webb","lend","mire","jose","catt","grow","toss","magi","leis","bead","kara","hoof","than","ires","baas","vein","kari","riga","oars","gags","thug","yawn","wive","view","germ","flab","july","tuck","rory","bean","feed","rhee","jeez","gobs","lath","desk","yoko","cute","zeus","thus","dims","link","dirt","mara","disc","limy","lewd","maud","duly","elsa","hart","rays","rues","camp","lack","okra","tome","math","plug","monk","orly","friz","hogs","yoda","poop","tick","plod","cloy","pees","imps","lead","pope","mall","frey","been","plea","poll","male","teak","soho","glob","bell","mary","hail","scan","yips","like","mull","kory","odor","byte","kaye","word","honk","asks","slid","hopi","toke","gore","flew","tins","mown","oise","hall","vega","sing","fool","boat","bobs","lain","soft","hard","rots","sees","apex","chan","told","woos","unit","scow","gilt","beef","jars","tyre","imus","neon","soap","dabs","rein","ovid","hose","husk","loll","asia","cope","tail","hazy","clad","lash","sags","moll","eddy","fuel","lift","flog","land","sigh","saks","sail","hook","visa","tier","maws","roeg","gila","eyes","noah","hypo","tore","eggs","rove","chap","room","wait","lurk","race","host","dada","lola","gabs","sobs","joel","keck","axed","mead","gust","laid","ends","oort","nose","peer","kept","abet","iran","mick","dead","hags","tens","gown","sick","odis","miro","bill","fawn","sumo","kilt","huge","ores","oran","flag","tost","seth","sift","poet","reds","pips","cape","togo","wale","limn","toll","ploy","inns","snag","hoes","jerk","flux","fido","zane","arab","gamy","raze","lank","hurt","rail","hind","hoot","dogy","away","pest","hoed","pose","lose","pole","alva","dino","kind","clan","dips","soup","veto","edna","damp","gush","amen","wits","pubs","fuzz","cash","pine","trod","gunk","nude","lost","rite","cory","walt","mica","cart","avow","wind","book","leon","life","bang","draw","leek","skis","dram","ripe","mine","urea","tiff","over","gale","weir","defy","norm","tull","whiz","gill","ward","crag","when","mill","firs","sans","flue","reid","ekes","jain","mutt","hems","laps","piss","pall","rowe","prey","cull","knew","size","wets","hurl","wont","suva","girt","prys","prow","warn","naps","gong","thru","livy","boar","sade","amok","vice","slat","emir","jade","karl","loyd","cerf","bess","loss","rums","lats",&#
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值