今日份题目:
字典 wordList
中从单词 beginWord
和 endWord
的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk
:
-
每一对相邻的单词只差一个字母。
-
对于
1 <= i <= k
时,每个si
都在wordList
中。注意,beginWord
不需要在wordList
中。 -
sk == endWord
给你两个单词 beginWord
和 endWord
和一个字典 wordList
,返回 从 beginWord
到 endWord
的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 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
-
beginWord
、endWord
和wordList[i]
由小写英文字母组成 -
beginWord != endWord
-
wordList
中的所有字符串 互不相同
题目思路
首先考虑建图,其次考虑遍历图。
我们把单词抽象成虚拟点,然后将单词拆分成只改变一个字母的虚拟节点。如:对于单词 hit,我们创建三个虚拟节点 * it、h * t、hi *,并让 hit 向这三个虚拟节点分别连一条边。如果一个单词能够转化为 hit,那么该单词必然会连接到这三个虚拟节点之一。对于每个单词,枚举它连接到的所有的虚拟节点,把该单词节点与这些虚拟节点相连即可。
最后进行广度优先遍历,当遍历到终点时,就找到了最短路径的长度。
注意:由于添加了虚拟节点,得到的距离为实际最短路径长度的两倍。同时由于并未计算起点的贡献,所以应当返回距离的一半再加一。
代码
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,大家一起在评论区打卡呀!