[LeetCode] 127. Word Ladder 词语阶梯
A transformation sequence from word beginWord
to word endWord
using a dictionary wordList
is a sequence of words beginWord -> s1 -> s2 -> ... -> sk
such that:
- Every adjacent pair of words differs by a single letter.
- Every
si
for1 <= i <= k
is inwordList
. Note thatbeginWord
does not need to be inwordList
. sk == endWord
Given two words, beginWord
and endWord
, and a dictionary wordList
, return the number of words in the shortest transformation sequence from beginWord
to endWord
, or 0
if no such sequence exists.
Example 1:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.
Example 2:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] Output: 0 Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.
Constraints:
1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord
,endWord
, andwordList[i]
consist of lowercase English letters.beginWord != endWord
- All the words in
wordList
are unique.
这道词句阶梯的问题给了我们一个单词字典,里面有一系列很相似的单词,然后给了一个起始单词和一个结束单词,每次变换只能改变一个单词,并且中间过程的单词都必须是单词字典中的单词,让求出最短的变化序列的长度。这道题还是挺有难度的,博主当然是看了别人的解法才写出来的,这没啥的,从不会到完全掌握才是成长嘛~
当拿到题就懵逼的我们如何才能找到一个科学的探索解题的路径呢,那就是先别去管代码实现,如果让我们肉身解题该怎么做呢?让你将 'hit' 变为 'cog',那么可以发现这两个单词没有一个相同的字母,所以就尝试呗,博主会先将第一个 'h' 换成 'c',看看 'cit' 在不在字典中,发现不在,那么把第二个 'i' 换成 'o',看看 'hot' 在不在,发现在,完美!然后尝试 'cot' 或者 'hog',发现都不在,那么就比较麻烦了,没法快速的达到目标单词,需要一些中间状态,但怎么知道中间状态是什么。简单粗暴的方法就是brute force,遍历所有的情况,将起始单词的每一个字母都用 26个 字母来替换,比如起始单词 'hit' 就要替换为 'ait', 'bit', 'cit', .... 'yit', 'zit',将每个替换成的单词都在字典中查找一下,如果有的话,那么说明可能是潜在的路径,要保存下来。
那么现在就有个问题,比如换到了 'hot' 的时候,此时发现在字典中存在,那么下一步是继续试接下来的 'hpt', 'hqt', 'hrt'... 还是直接从 'hot' 的首字母开始换 'aot', 'bot', 'cot' ... 这实际上就是 BFS 和 DFS 的区别,到底是广度优先,还是深度优先。讲到这里,不知道你有没有觉得这个跟什么很像?对了,跟迷宫遍历很像啊,你想啊,迷宫中每个点有上下左右四个方向可以走,而这里有 26 个字母,就是二十六个方向可以走,本质上没有啥区别啊!如果熟悉迷宫遍历的童鞋们应该知道,应该用 BFS 来求最短路径的长度,这也不难理解啊,DFS 相当于一条路走到黑啊,你走的那条道不一定是最短的啊。而 BFS 相当于一个小圈慢慢的一层一层扩大,相当于往湖里扔个石头,一圈一圈扩大的水波纹那种感觉,当水波纹碰到湖上的树叶时,那么此时水圈的半径就是圆心到树叶的最短距离。脑海中有没有浮现出这个生动的场景呢?
明确了要用 BFS,就可以开始解题了,为了提到字典的查找效率,这里使用 HashSet 保存所有的单词。然后需要一个 HashMap,来建立某条路径结尾单词和该路径长度之间的映射,并把起始单词映射为1。既然是 BFS,就需要一个队列 queue,把起始单词排入队列中,开始队列的循环,取出队首词,然后对其每个位置上的字符,用 26 个字母进行替换,如果此时和结尾单词相同了,就可以返回取出词在 HashMap 中的值加一。如果替换词在字典中存在但在 HashMap 中不存在,则将替换词排入队列中,并在 HashMap 中的值映射为之前取出词加一。如果循环完成则返回0,参见代码如下:
解法一:
class Solution { public: int ladderLength(string beginWord, string endWord, vector<string>& wordList) { unordered_set<string> wordSet(wordList.begin(), wordList.end()); if (!wordSet.count(endWord)) return 0; unordered_map<string, int> pathCnt{{{beginWord, 1}}}; queue<string> q{{beginWord}}; while (!q.empty()) { string word = q.front(); q.pop(); for (int i = 0; i < word.size(); ++i) { string newWord = word; for (char ch = 'a'; ch <= 'z'; ++ch) { newWord[i] = ch; if (wordSet.count(newWord) && newWord == endWord) return pathCnt[word] + 1; if (wordSet.count(newWord) && !pathCnt.count(newWord)) { q.push(newWord); pathCnt[newWord] = pathCnt[word] + 1; } } } } return 0; } };
其实我们并不需要上面解法中的 HashMap,由于 BFS 的遍历机制就是一层一层的扩大的,那么只要记住层数就行,然后在 while 循环中使用一个小 trick,加一个 for 循环,表示遍历完当前队列中的个数后,层数就自增1,这样的话就省去了 HashMap,而仅仅用一个变量 res 来记录层数即可,参见代码如下:
解法二:
class Solution { public: int ladderLength(string beginWord, string endWord, vector<string>& wordList) { unordered_set<string> wordSet(wordList.begin(), wordList.end()); if (!wordSet.count(endWord)) return 0; queue<string> q{{beginWord}}; int res = 0; while (!q.empty()) { for (int k = q.size(); k > 0; --k) { string word = q.front(); q.pop(); if (word == endWord) return res + 1; for (int i = 0; i < word.size(); ++i) { string newWord = word; for (char ch = 'a'; ch <= 'z'; ++ch) { newWord[i] = ch; if (wordSet.count(newWord) && newWord != word) { q.push(newWord); wordSet.erase(newWord); } } } } ++res; } return 0; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/127
类似题目:
参考资料:
https://leetcode.com/problems/word-ladder/
https://leetcode.com/problems/word-ladder/discuss/40728/Simple-Java-BFS-solution-with-explanation