[LeetCode] 127.Word Ladder 词语阶梯
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
- Only one letter can be changed at a time.
- Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
这道词句阶梯的问题给了我们一个单词字典,里面有一系列很相似的单词,然后给了一个起始单词和一个结束单词,每次变换只能改变一个单词,并且中间过程的单词都必须是单词字典中的单词,让我们求出最短的变化序列的长度。这道题还是挺有难度的,我当然是看了别人的解法才写出来的,这没啥的,从不会到完全掌握才是成长嘛~
当拿到题就懵逼的我们如何才能找到一个科学的探索解题的路径呢,那就是先别去管代码实现,如果让我们肉身解题该怎么做呢?让你将 '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个字母进行替换,如果此时和结尾单词相同了,就可以返回取出词在哈希表中的值加一。如果替换词在字典中存在但在哈希表中不存在,则将替换词排入队列中,并在哈希表中的值映射为之前取出词加一。如果循环完成则返回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;
}
};
类似题目:
Word Ladder II
Minimum Genetic Mutation
参考资料:
https://leetcode.com/problems/word-ladder/description/
https://leetcode.com/problems/word-ladder/discuss/40728/Simple-Java-BFS-solution-with-explanation
到此这篇关于C++实现LeetCode(127.词语阶梯)的文章就介绍到这了,更多相关C++实现词语阶梯内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!