[H图论+bfs] lc126. 单词接龙 II(图论难题+建图+bfs求最短路)

文章目录

    • 1. 题目来源
    • 2. 题目说明
    • 3. 题目解析

1. 题目来源

链接:126. 单词接龙 II

2. 题目说明

[H图论+bfs] lc126. 单词接龙 II(图论难题+建图+bfs求最短路)_第1张图片

3. 题目解析

本题很难。考查图论建图、单源最短路、bfs 求解边权为 1 的最短路。

思路:

  • 将每个单词看成图论中的一个点,如果这个单词能在一步之内变成某个单词。那么就在这两个单词中连一条边。这样初步就能建出一个无向图,将其转化为单源最短路问题,且由于边权为 1,那么 bfs 求解最短路就可以使用了。
  • 最小转化步数即为最短路,就可以求解得到了。但是本题是需要输出所有方案[H图论+bfs] lc126. 单词接龙 II(图论难题+建图+bfs求最短路)_第2张图片
    类似于上图,那么这个输出的方案数就是指数级别的。所以使用 dfs 暴力搜索的话就是指数级别的。所以不管怎么优化,答案数量都是指数级别的,所以必须考虑剪枝
  • 建立 dist 数组,dist[i] 表示从起点到 i 的最短路,可以通过 bfs 搜索得到所有点到起点的最短路
  • 利用 dist 数组暴搜出所有最短路径。采用反向搜索, 从终点 T 开始搜索,枚举所有邻点 v,若 dist[v] + 1 == dist[T],说明存在一条可能的最短路径,那么我们就走到 v 去,即递归 dfs(v) 。这样就不会去搜索不合法方案,是一步很厉害的剪枝
  • 最后一步是从 v 走到 T,当 dfs 参数与起点相同就说明我们成功的从终点沿着最短路找到了起点,找到了这条答案路径。返回路径数组即可

两种建图方式:

  • 可以两两枚举两个单词,查看是否仅有一个单词不同,这样的建图方式就是 O ( n 2 L ) O(n^2L) O(n2L)
  • 也可以每个单词的每一个字母总共 26 种情况变换,再将变换单词插入到哈希表中,需要进行查询其是否已经存在。总共的时间复杂度为 O ( 26 n L 2 ) O(26nL^2) O(26nL2)
  • 所以,对比两种建图方式,当 n>= 26L 时,采用第二种建图方式,否则采用第一种方式比较好

这题是很棒的一道图论问题,考点还是很全面的。从问题抽象,建图,最短路,确实很有价值!

一道很是类似的题目,推荐:[bfs+图论] 八数码(建图+bfs最短路+思维)

代码:

class Solution {
public:
    unordered_set<string> S;
    unordered_map<string, int> dist;
    queue<string> q;
    vector<vector<string>> ans;
    vector<string> path;
    string start;

    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        start = beginWord;
        for (auto word:wordList) S.insert(word);

        dist[beginWord] = 0;
        q.push(beginWord);
        while (q.size()) {
            auto t = q.front();
            q.pop();

            string r = t;                                   // 缓存t字符串,防止改变影响后序
            for (int i = 0; i < t.size(); ++i) {            // 枚举当前单词每一位
                t = r;                                      // 每次拿到初始的t
                for (char j = 'a'; j <= 'z'; ++j) {         // 枚举当前单词改变的26种情况
                    t[i] = j;                               // 改变当前位,t字符串更新
                    if (S.count(t) && dist.count(t) == 0) { // t是一次成功更新,且dist没有t,说明r之前没有边到t
                        dist[t] = dist[r] + 1;              // 更新最短路距离+1
                        if (t == endWord) break;            // 如果t是终点则不必要找终点的转化,直接break
                        q.push(t);                          // t更新后入队  
                    }
                }
            }
        }

        if (dist.count(endWord) == 0) return ans;          // 如果终点不在图中,直接返回
        path.push_back(endWord);                           // 将终点加入答案中
        dfs(endWord);                                      // dfs从终点开始搜
        return ans;                                        
    }

    // 这里是从终点往起点搜,故ans存的是逆序
    void dfs(string t) {
        if (t == start) {                                  // 如果当前点是起始点
            reverse(path.begin(), path.end());
            ans.push_back(path);
            reverse(path.begin(), path.end());
        } else {
            string r = t;                                  // 当前单词
            for (int i = 0; i < t.size(); ++i) {           // 枚举当前单词所转化的所有单词
                t = r;
                for (int j = 'a'; j <= 'z'; ++j) {
                    t[i] = j;
                    if (dist.count(t) && dist[t] + 1 == dist[r]) {  // 新t存在且在可能的最短路径中
                        path.push_back(t);                 // 将t加入答案
                        dfs(t);                            // 以t作为终点,dfs处理
                        path.pop_back();                   // 回溯
                    }
                }
            }
        }
    }
};

你可能感兴趣的:(#,bfs最短路模型,LeetCode,LeetCode,图论)