首页 > 其他分享 >【搜索】力扣126:单词接龙 II(过于hard)

【搜索】力扣126:单词接龙 II(过于hard)

时间:2022-08-20 20:36:50浏览次数:84  
标签:wordList 力扣 遍历 int hard II beginWord endWord 单词

给定一个起始字符串和一个终止字符串,以及一个单词表,求是否可以将起始字符串每次改一个字符,直到改成终止字符串,且所有中间的修改过程表示的字符串都可以在单词表里找到。若存在,输出需要修改次数最少的所有更改方式。

输入是两个字符串,输出是一个二维字符串数组,表示每种字符串修改方式。

示例:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
解释:存在 2 种最短的转换序列:
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
"hit" -> "hot" -> "lot" -> "log" -> "cog"

可以把起始字符串、终止字符串、以及单词表里所有的字符串想象成结点。若两个字符串只有一个字符不同,那么它们相连。因为题目需要输出修改次数最少的所有修改方式,看到“最短”、“最少”就想到 BFS,求得起始结点到终止结点的最短距离。

(可以用DFS,但是时间过长,具体可以看这篇Java题解 详细通俗的思路分析,多解法

如果两个单词可以只改变一个字母进行转换,那么说明两个结点之间有一条双向边(原因:如果可以从一个单词 word1 转换成为单词 word2,那么一定可以从单词 word2 转换成为 word1)。因此只需要把满足转换条件的点相连,就形成了一张无向图。
image

这样就是以 “hit" 为图的起点, 以 “cog" 为终点进行广度优先搜索,寻找 “hit" 到 “cog" 的最短路径。

由于要求输出所有的最短路径,因此需要记录遍历路径,然后通过回溯得到所有的最短路径。由于要找“最短”,如果位于广度优先遍历同一层的单词之间有边连接,不可以被记录下来。
image

但是同时要记录路径,因此需要一个哈希表记录遍历到的单词在第几层。
image

综上,在广度优先遍历的时候需要记录:从当前的单词 currWord 只变化了一个字符以后,且又在单词字典中的单词 nextWord 之间的单向关系(虽然实际上无向图,但是广度优先遍历是有方向的,解决这个问题可以只看成有向图),记为 from,它是一个映射关系:键是单词,值是广度优先遍历的时候从哪些单词可以遍历到「键」所表示的单词,使用哈希表来保存。

双层BFS

可以使用一个小技巧:不直接从起始结点进行广度优先搜索,直到找到终止结点为止;而是从起始结点和终止结点分别进行广度优先搜索,每次只延展当前层结点数最少的那一端,这样就可以减少搜索的总结点数。

举例:假设最短距离为 4,如果只从一端搜索 4 层,总遍历结点数最多是 1 + 2 + 4 + 8 + 16 = 31;而如果从两端各搜索两层,总遍历结点数最多只有 2 × (1 + 2 + 4) = 14

class Solution:
    def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
        if endWord not in wordList:
            return []
        qu = collections.deque([(beginWord, [[beginWord]])])
        while qu:
            s = len(qu)
            qui = {}
            for i in range(s):
                a = qu.popleft()
                if a[0] == endWord:
                    return a[1]
                f = set()
                for wi in range(len(wordList)):
                    if wordList[wi] == '0':
                        continue
                    sa = 0
                    for j in range(len(a[0])):
                        if a[0][j] != wordList[wi][j]:
                            sa += 1
                        if sa > 1:
                            break
                    if sa == 1:
                        if wordList[wi] not in qui:
                            qui[wordList[wi]] = [ai + [wordList[wi]] for ai in a[1]]
                        else:
                            qui[wordList[wi]] = qui[wordList[wi]] + [ai + [wordList[wi]] for ai in a[1]]
                        f.add(wi)
            for quik in qui:
                qu.append((quik, qui[quik]))
                wordList[wordList.index(quik)] = '0'

        return []

超时。

BFS + DFS回溯

广度优先遍历建图 + 深度优先遍历找到所有解(Java)
坐等大佬再更新双层BFS

@Java

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

public class Solution {

    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> res = new ArrayList<>();
        // 因为需要快速判断扩展出的单词是否在 wordList 里,因此需要将 wordList 存入哈希表,这里命名为「字典」
        Set<String> dict = new HashSet<>(wordList);
        // 特殊用例判断
        if (!dict.contains(endWord)) {
            return res;
        }
        // 因为从 beginWord 开始扩展,因此 dict 里一定不可以有 beginWord
        dict.remove(beginWord);

        // 第 1 步:广度优先遍历构建图
        // 为了避免记录不需要的边,我们需要记录扩展出的单词是在第几次扩展的时候得到的,key:单词,value:在广度优先遍历的第几层
        // steps 记录了已经访问过的 word 集合,同时记录了在第几层访问到
        Map<String, Integer> steps = new HashMap<>();
        steps.put(beginWord, 0);
        // 记录了单词是从哪些单词扩展而来,key:单词,value:单词列表,这些单词可以变换到 key ,它们是一对多关系,dfs 的时候会用到
        Map<String, Set<String>> from = new HashMap<>();
        boolean found = bfs(beginWord, endWord, dict, steps, from);

        // 第 2 步:深度优先遍历找到所有解,从 endWord 恢复到 beginWord ,所以每次尝试操作 path 列表的头部
        if (found) {
            Deque<String> path = new ArrayDeque<>();
            path.add(endWord);
            dfs(from, path, beginWord, endWord, res);
        }
        return res;
    }


    private boolean bfs(String beginWord, String endWord, Set<String> dict, Map<String, Integer> steps, Map<String, Set<String>> from) {
        int wordLen = beginWord.length();
        int step = 0;
        boolean found = false;

        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        while (!queue.isEmpty()) {
            step++;
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String currWord = queue.poll();
                char[] charArray = currWord.toCharArray();
                for (int j = 0; j < wordLen; j++) {
                    char origin = charArray[j];
                    for (char c = 'a'; c <= 'z'; c++) {
                        // 将每一位替换成 26 个小写英文字母
                        charArray[j] = c;
                        String nextWord = String.valueOf(charArray);
                        // 注意:这几行代码的逻辑先后顺序
                        if (steps.containsKey(nextWord) && steps.get(nextWord) == step) {
                            from.get(nextWord).add(currWord);
                        }

                        if (!dict.contains(nextWord)) {
                            continue;
                        }
                        dict.remove(nextWord);

                        // dict 和 steps 承担了已经访问的功能
                        queue.offer(nextWord);

                        // 维护 from、steps、found 的定义
                        from.putIfAbsent(nextWord, new HashSet<>());
                        from.get(nextWord).add(currWord);
                        steps.put(nextWord, step);
                        if (nextWord.equals(endWord)) {
                            // 注意:由于有多条路径到达 endWord 找到以后不能立即退出,只需要设置 found = true 即可
                            found = true;
                        }
                    }
                    charArray[j] = origin;
                }
            }
            if (found) {
                break;
            }
        }
        return found;
    }

    private void dfs(Map<String, Set<String>> from, Deque<String> path, String beginWord, String cur, List<List<String>> res) {
        if (cur.equals(beginWord)) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (String precursor : from.get(cur)) {
            path.addFirst(precursor);
            dfs(from, path, beginWord, precursor, res);
            path.removeFirst();
        }
    }
}

作者:liweiwei1419
链接:https://leetcode.cn/problems/word-ladder-ii/solution/yan-du-you-xian-bian-li-shuang-xiang-yan-du-you--2/

Dijkstra求最短路径 + DFS重建单词转换路径(C++)

bfs 就是边的权重都为 1 时的 Dijkstra

class Solution {
  public:
    vector<vector<string>> findLadders(string beginWord, string endWord,
                                       vector<string> &wordList) {
        vector<vector<string>> ret;
        auto it = find(wordList.begin(),wordList.end(), endWord);
        if (it == wordList.end())
            return ret;
        int src, dst;
        dst = it - wordList.begin();

        it = find(wordList.begin(),wordList.end(), beginWord);
        if (it == wordList.end()) {
            wordList.push_back(beginWord);
            src = wordList.size() - 1;
        } else {
            src = it - wordList.begin();
        }

        int wordLen = beginWord.size();
        // 构建邻接表
        vector<vector<int>> graph(wordList.size());
        for (int i = 0; i < wordList.size(); i++) {
            for (int j = i + 1; j < wordList.size(); j++) {
                int cnt = 0;
                for (int k = 0; k < wordLen; ++k) {
                    if (wordList[i][k] != wordList[j][k])
                        cnt++;
                    if (cnt > 1)
                        break;
                }

作者:destinygang-UtZgjuB0v8
链接:https://leetcode.cn/problems/word-ladder-ii/solution/by-destinygang-utzgjub0v8-2tdu/

标签:wordList,力扣,遍历,int,hard,II,beginWord,endWord,单词
From: https://www.cnblogs.com/Jojo-L/p/16595379.html

相关文章

  • 【队列】力扣239:滑动窗口最大值
    给定一个整数数组和一个滑动窗口大小,求在这个窗口的滑动过程中,每个时刻其包含的最大值。示例:输入:nums=[1,3,-1,-3,5,3,6,7],k=3输出:[3,3,5,5,6,7]解释:滑动窗口......
  • 【队列】力扣218:天际线问题
    给定建筑物的起止位置和高度,返回建筑物轮廓(天际线)的拐点。输入是一个二维整数数组,表示每个建筑物的[左端,右端,高度];输出是一个二维整数数组,表示每个拐点的横纵坐标。......
  • 基于温度检测工程:ascii_2_hex 练习
    因工作关系,中间隔好久没写代码,感觉有点生疏了。看来还是得多写写才行!!当串口输入的是ascii值时,FPGA内部收到的数据需将ascii转为十六进制。 在0~9、A~F、a~f范围内是......
  • 开发H5程序或者小程序的时候,后端Web API项目在IISExpress调试中使用IP地址,便于开发调
    在我们开发开发H5程序或者小程序的时候,有时候需要基于内置浏览器或者微信开发者工具进行测试,这个时候可以采用默认的localhost进行访问后端接口,一般来说没什么问题,如果我们......
  • Codeforces Round #815 (Div. 2) D2 Xor-Subsequence (hard version)
    原题链接\(A>B\),总是有二进制下从高到低的前\(k\)位相等,第\(k+1\)位\(A\)是\(1\),\(B\)是\(0\)本题中\(A=a_i\oplusj\),\(B=a_j\oplusi\),这里有一个很奇妙的性质(手玩或者......
  • CF1196D2 RGB Substring (hard version)
    https://www.luogu.com.cn/problem/CF1196D2前缀和黄色题思路:看当前输入要被修改的这个字符串的第i位,是否与'R','G','B'三个中的一个相等,不相等的另外两个则增加一次修......
  • D2. Xor-Subsequence (hard version)
    D2.Xor-Subsequence(hardversion)昨天cf的E题,挺好的一个DP优化问题。暴力的DP就是设dp[i]表示以i结尾的最长长度。转移时枚举之前的所有j,复杂度O(n^2)。考虑怎么优......
  • SP6779 GSS7 - Can you answer these queries VII
    GSS7-CanyouanswerthesequeriesVIIGSS7(Luogu)题面翻译题目描述给定一棵树,有\(N(N\le100000)\)个节点,每一个节点都有一个权值\(x_i(|x_i|\le10000)\)你......
  • 千兆以太网_发送模块设计_udp_rgmii_tx
    功能:在FPGA开发板上,用户数据存于FIFO,经过UDP,IP,MAC封装,通过RGMII接口发送出去。完整的以太网应该包括收发功能,这里介绍发送模块。实现:序列机过程:发送顺序:  MAC帧头—......
  • 一张图看懂 OrchardCore 中的模块加载及依赖管理
    先上图   Manifest.cs  Module与FeatureModule特性 如果模块中只有一个功能【Feature】那么可以直接用Module替代,也就是///<summary>///......