每天一道leetcode:127. 单词接龙(图论&困难&建图&广度优先遍历)

今日份题目:

字典 wordList 中从单词 beginWordendWord转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk

  • 每一对相邻的单词只差一个字母。

  • 对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。

  • sk == endWord

给你两个单词 beginWordendWord 和一个字典 wordList ,返回 beginWordendWord最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0

示例1

复制代码
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:5
解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。

示例2

复制代码
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:0
解释:endWord "cog" 不在字典中,所以无法进行转换。

提示

  • 1 <= beginWord.length <= 10

  • endWord.length == beginWord.length

  • 1 <= wordList.length <= 5000

  • wordList[i].length == beginWord.length

  • beginWordendWordwordList[i] 由小写英文字母组成

  • beginWord != endWord

  • wordList 中的所有字符串 互不相同

题目思路

首先考虑建图,其次考虑遍历图。

我们把单词抽象成虚拟点,然后将单词拆分成只改变一个字母的虚拟节点。如:对于单词 hit,我们创建三个虚拟节点 * it、h * t、hi *,并让 hit 向这三个虚拟节点分别连一条边。如果一个单词能够转化为 hit,那么该单词必然会连接到这三个虚拟节点之一。对于每个单词,枚举它连接到的所有的虚拟节点,把该单词节点与这些虚拟节点相连即可。

最后进行广度优先遍历,当遍历到终点时,就找到了最短路径的长度。

注意:由于添加了虚拟节点,得到的距离为实际最短路径长度的两倍。同时由于并未计算起点的贡献,所以应当返回距离的一半再加一。

代码

cpp 复制代码
class Solution 
{
public:
    unordered_map<string,int> dict; //存放字典内容
    vector<vector<int>> edge;
    int nodeNum=0; //用来标记是第几个点

    void wordNode(string& word) //把单词抽象为一个点
    {
        if(!dict.count(word)) //字典中没有这个点
        {
            dict[word]=nodeNum;
            nodeNum++;
            edge.emplace_back();
        }
    }

    void Edge(string& word) //如果两个单词可以通过只改变一个字母实现转换,表示两点之间有条边
    {
        wordNode(word); //建立点
        int u=dict[word]; //边的第一个端点
        for(char& c:word) //遍历所有位置,得到各虚拟节点*it、h*t、hi*
        {
            //hit<->*it,hit<->h*t,hit<->hi*
            char temp=c;
            c='*';
            wordNode(word); //分别建立虚拟点
            int v=dict[word]; //边的第二个端点
            //建立无向图的边
            edge[u].push_back(v);
            edge[v].push_back(u);
            c=temp; //恢复原单词
        }
    }

    int ladderLength(string beginWord, string endWord, vector<string>& wordList) 
    {
        for(string& word:wordList) //对字典中的每个单词建立边
        {
            Edge(word);
        }
        Edge(beginWord); //对初始单词建立边
        if(!dict.count(endWord)) return 0; //无法变化为结果的样子
        //注意,由于是在建立边的过程中建立虚拟点,所以一定要先建好边再判断能否变化为结果单词
        vector<int> dis(nodeNum,INT_MAX); //记录变化步数
        int begin=dict[beginWord],end=dict[endWord]; //开始的点和结束的点
        dis[begin]=0;
        queue<int> p;
        p.push(begin);
        //bfs
        while(!p.empty()) 
        {
            int cur=p.front();
            p.pop();
            if(cur==end) 
            {
                //因为添加了虚拟节点,所以得到的距离为实际最短路径长度的两倍;同时由于未计算起点的贡献,所以应当返回距离的一半再加一
                return dis[end]/2+1;
            }
            for(int& c:edge[cur]) //遍历当前单词可以进行的变化
            {
                //如果一个单词能够转化为 hit,那么该单词必然会连接到上述三个虚拟节点之一
                if(dis[c]==INT_MAX) //还没到过这个单词变化情况
                {
                    dis[c]=dis[cur]+1; //这种情况是在原来情况下变一个字母,步数加一
                    p.push(c);
                }
            }
        }
        return 0;
    }
};

提交结果

欢迎大家在评论区讨论,如有不懂的部分,欢迎在评论区留言!

更新不易,宝子们点个赞支持下,谢谢!

每天一道leetcode,大家一起在评论区打卡呀!

相关推荐
我是谁??几秒前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
小码农<^_^>3 分钟前
优选算法精品课--滑动窗口算法(一)
算法
羊小猪~~5 分钟前
神经网络基础--什么是正向传播??什么是方向传播??
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
软工菜鸡30 分钟前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
南宫生33 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
发霉的闲鱼34 分钟前
MFC 重写了listControl类(类名为A),并把双击事件的处理函数定义在A中,主窗口如何接收表格是否被双击
c++·mfc
小c君tt37 分钟前
MFC中Excel的导入以及使用步骤
c++·excel·mfc
xiaoxiao涛44 分钟前
协程6 --- HOOK
c++·协程
AI视觉网奇1 小时前
sklearn 安装使用笔记
人工智能·算法·sklearn
JingHongB1 小时前
代码随想录算法训练营Day55 | 图论理论基础、深度优先搜索理论基础、卡玛网 98.所有可达路径、797. 所有可能的路径、广度优先搜索理论基础
算法·深度优先·图论