LintCode 121. 单词接龙 II

2020-07-29  本文已影响0人  CW不要无聊的风格

题目描述

给出两个单词(startend)和一个字典,找出所有从startend的最短转换序列。

变换规则如下:

1. 每次只能改变一个字母。

2. 变换过程中的中间单词必须在字典中出现。

注意事项

所有单词具有相同的长度。

所有单词都只包含小写字母。

题目确保存在合法的路径。


测试样例

输入:start = "a",end = "c",dict =["a","b","c"]

输出:[["a","c"]]

解释:

"a"->"c"

输入:start ="hit",end = "cog",dict =["hot","dot","dog","lot","log"]

输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]

解释:

1."hit"->"hot"->"dot"->"dog"->"cog"

2."hit"->"hot"->"lot"->"log"->"cog"

第一个序列的字典序小于第二个。


解题思路

首先从end开始反向构建层级关系,提供可行的所有路径,既然是层级关系,那么就很自然会使用BFS

在BFS对每层进行搜索时,需要记录下搜索的下一层每个单词距end的距离,可用一个dict来记录,此处记为distance,key就是搜索到的下一层单词,value是对应距end的距离,设置为上一层单词距离加1。另外,搜索到的词还需要满足之前没有被distance记录,即不在distance的key中,避免构建循环路径。

这里讲下搜索单词的方法:

对单词的每个字母位置依次进行替换,每个位置有25种可能(26个字母除去当前字母),然后判断替换后的词是否在字典中。如果是,则作为搜索到的下一个单词。

BFS构建完层级关系后,就从start开始进行DFS,搜寻符合要求的每条目标路径。DFS搜索单词的方法如同上述,但是需要外加一个条件——搜索到的下个词距离end必须比当前这个词更近1。最后搜索至当前的词为end后将整条路径加入结果集并终止搜索。

另外,还有一种搜索单词的方法:

对字典中的每个单词,将其每个位置的字母用'%'代替,构建一个patten,作为key,value设置为原来的这个单词,将这样的一个个key-value对存到一个dict中,之后在BFS和DFS中就通过这个dict来查询搜索的词,而不再使用原先的字典。


代码

1.

from collections import deque

class Solution:

    """

    @param: start: a string

    @param: end: a string

    @param: dict: a set of string

    @return: a list of lists of string

    """

    L = sys.maxsize

    def findLadders(self, start, end, dict):

        if len(start) == 1 and len(end) == 1:

            return [[start, end]]

        # 记录路径中各个词到end的距离       

        dis = {}

        # 将start,end加入字典,注意不要写成.union(start, end)!

        dict = dict.union([start, end])

        # 从end开始反向进行宽度优先搜索,建立层级关系

        self._BFS(end, dis, dict)

        result = []

        # 从start开始进行深搜,寻找每条目标路径

        self._DFS(start, end, dis, dict, [start], result)

        return result

    def _DFS(self, cur, target, dis, dic, path, result):

        if cur == target:

            result.append(path)

            return

        for next_word in self._get_next_words(cur, dic):

            # 必须满足下一个词的距离end比当前词距离end更近1

            if dis[cur] - dis[next_word] == 1:

                self._DFS(next_word, target, dis, dic, path + [next_word], result)

    def _BFS(self, begin, dis, dic):

        dis[begin] = 0

        queue = deque([begin])

        while queue:

            cur = queue.popleft()

            for next_word in self._get_next_words(cur, dic):

                # 注意下一个词需要未被dis记录

                # 避免构建循环路径

                if next_word not in dis:

                    dis[next_word] = dis[cur] + 1

                    queue.append(next_word)

    def _get_next_words(self, word, dic):

        """寻找下一个可能的词,其必须在字典中"""

        next_words = []

        for i in range(len(word)):

            for c in 'abcdefghijklmnopqrstuvwxyz':

                if c == word[i]:

                    continue

                next_word = word[:i] + c + word[i + 1:]

                if next_word in dic:

                    next_words.append(next_word)

        return next_words

2.

from collections import deque

class Solution:

    """

    @param: start: a string

    @param: end: a string

    @param: dict: a set of string

    @return: a list of lists of string

    """

    L = sys.maxsize

    def findLadders(self, start, end, dict):

        if len(start) == 1 and len(end) == 1:

            return [[start, end]]

        # 将start,end加入字典,注意不要写成.union(start, end)!

        dict = dict.union([start, end])

        # 对字典中每个词的替换模式都构建对应的映射关系

        # 将每个词的各个位置替换为'%'作为key,value是该词本身

        maps = self._build_maps(dict)

        # 从end开始反向进行宽度优先搜索,建立层级关系

        dis = self._BFS(end, maps)

        result = []

        # 从start开始进行深搜,寻找每条目标路径

        self._DFS(start, end, dis, maps, [start], result)

        return result

    def _build_maps(self, dic):

        maps = {}

        for word in dic:

            for i in range(len(word)):

                pattern = word[:i] + '%' + word[i + 1:]

                if pattern in maps:

                    maps[pattern].add(word)

                else:

                    maps[pattern] = set([word])

        return maps

    def _DFS(self, cur, target, dis, maps, path, result):

        if cur == target:

            result.append(path)

            return

        for next_word in self._get_next_words(cur, maps):

            # 必须满足下一个词的距离end比当前词距离end更近1

            if dis[cur] - dis[next_word] == 1:

                self._DFS(next_word, target, dis, maps, path + [next_word], result)

    def _BFS(self, begin, maps):

        # 记录路径中各词到目标的距离

        dis = {begin: 0}

        queue = deque([begin])

        while queue:

            cur = queue.popleft()

            for next_word in self._get_next_words(cur, maps):

                # 注意下一个词需要未被dis记录

                # 避免构建循环路径

                if next_word not in dis:

                    dis[next_word] = dis[cur] + 1

                    queue.append(next_word)

        return dis

    def _get_next_words(self, word, maps):

        """寻找下一个可能的词,其必须在字典中"""

        next_words = []

        for i in range(len(word)):

            next_key = word[:i] + '%' + word[i + 1:]

            for next_word in maps.get(next_key, []):

                next_words.append(next_word)

        return next_words

上一篇下一篇

猜你喜欢

热点阅读