LeetCode:Word Ladder I II
其他LeetCode题目欢迎访问:LeetCode结题报告索引
Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
- Only one letter can be changed at a time
- Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
分析:这种题,肯定是每次改变单词的一个字母,然后逐渐搜索,很多人一开始就想到用dfs,其实像这种求最短路径、树最小深度问题bfs最适合,可以参考我的这篇博客bfs(层序遍历)求二叉树的最小深度。本题bfs要注意的问题:
- 和当前单词相邻的单词是:对当前单词改变一个字母且在字典中存在的单词
- 找到一个单词的相邻单词,加入bfs队列后,要从字典中删除,因为不删除的话会造成类似于hog->hot->hog的死循环。而删除对求最短路径没有影响,因为我们第一次找到该单词肯定是最短路径,即使后面其他单词也可能转化得到它,路径肯定不会比当前的路径短(如果要输出所有最短路径,则不能立即从字典中删除,具体见下一题)
- bfs队列中用NULL来标识层与层的间隔,每次碰到层的结尾,遍历深度+1
我们利用和求二叉树最小深度层序遍历的方法来进行bfs,代码如下: 本文地址
class Solution {
public:
int ladderLength(string start, string end, unordered_set<string> &dict) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
//BFS遍历找到的第一个匹配就是最短转换,空字符串是层与层之间的分隔标志
queue<string> Q;
Q.push(start); Q.push("");
int res = ;
while(Q.empty() == false)
{
string str = Q.front();
Q.pop();
if(str != "")
{
int strLen = str.length();
for(int i = ; i < strLen; i++)
{
char tmp = str[i];
for(char c = 'a'; c <= 'z'; c++)
{
if(c == tmp)continue;
str[i] = c;
if(str == end)return res+;
if(dict.find(str) != dict.end())
{
Q.push(str);
dict.erase(str);
}
}
str[i] = tmp;
}
}
else if(Q.empty() == false)
{//到达当前层的结尾,并且不是最后一层的结尾
res++;
Q.push("");
}
}
return ;
}
};
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
- Only one letter can be changed at a time
- Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
Note:
- All words have the same length.
- All words contain only lowercase alphabetic characters.
分析:本题主要的框架和上一题是一样,但是还要解决两个额外的问题:一、 怎样保证求得所有的最短路径;二、 怎样构造这些路径
第一问题:
- 不能像上一题第二点注意那样,找到一个单词相邻的单词后就立马把它从字典里删除,因为当前层还有其他单词可能和该单词是相邻的,这也是一条最短路径,比如hot->hog->dog->dig和hot->dot->dog->dig,找到hog的相邻dog后不能立马删除,因为和hog同一层的单词dot的相邻也是dog,两者均是一条最短路径。但是为了避免进入死循环,再hog、dot这一层的单词便利完成后dog还是得从字典中删除。即等到当前层所有单词遍历完后,和他们相邻且在字典中的单词要从字典中删除。
- 如果像上面那样没有立马删除相邻单词,就有可能把同一个单词加入bfs队列中,这样就会有很多的重复计算(比如上面例子提到的dog就会被2次加入队列)。因此我们用一个哈希表来保证加入队列中的单词不会重复,哈希表在每一层遍历完清空(代码中hashtable)。
- 当某一层的某个单词转换可以得到end单词时,表示已经找到一条最短路径,那么该单词的其他转换就可以跳过。并且遍历完这一层以后就可以跳出循环,因为再往下遍历,肯定会超过最短路径长度
第二个问题:
- 为了输出最短路径,我们就要在比bfs的过程中保存好前驱节点,比如单词hog通过一次变换可以得到hot,那么hot的前驱节点就包含hog,每个单词的前驱节点有可能不止一个,那么每个单词就需要一个数组来保存前驱节点。为了快速查找因此我们使用哈希表来保存所有单词的前驱路径,哈希表的key是单词,value是单词数组。(代码中的unordered_map<string,vector<string> >prePath)
- 有了上面的前驱路径,可以从目标单词开始递归的构造所有最短路径(代码中的函数 ConstructResult)
class Solution {
public:
typedef unordered_set<string>::iterator HashIter;
vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
// Note: The Solution object is instantiated only once and is reused by each test case.
queue<string> Q;
Q.push(start); Q.push("");
bool hasFound = false;
unordered_map<string,vector<string> >prePath;//前驱路径
unordered_set<string> hashtable;//保证bfs时插入队列的元素不存在重复
while(Q.empty() == false)
{
string str = Q.front(), strCopy = str;
Q.pop();
if(str != "")
{
int strLen = str.length();
for(int i = ; i < strLen; i++)
{
char tmp = str[i];
for(char c = 'a'; c <= 'z'; c++)
{
if(c == tmp)continue;
str[i] = c;
if(str == end)
{
hasFound = true;
prePath[end].push_back(strCopy);
//找到了一条最短路径,当前单词的其它转换就没必要
goto END;
}
if(dict.find(str) != dict.end())
{
prePath[str].push_back(strCopy);
//保证bfs时插入队列的元素不存在重复
if(hashtable.find(str) == hashtable.end())
{Q.push(str); hashtable.insert(str);}
}
}
str[i] = tmp;
}
}
else if(Q.empty() == false)//到当前层的结尾,且不是最后一层的结尾
{
if(hasFound)break;
//避免进入死循环,把bfs上一层插入队列的元素从字典中删除
for(HashIter ite = hashtable.begin(); ite != hashtable.end(); ite++)
dict.erase(*ite);
hashtable.clear();
Q.push("");
}
END: ;
}
vector<vector<string> > res;
if(prePath.find(end) == prePath.end())return res;
vector<string> tmpres;
tmpres.push_back(end);
ConstructResult(prePath, res, tmpres, start, end);
return res;
}
private:
//从前驱路径中回溯构造path
void ConstructResult(unordered_map<string,vector<string> > &prePath,
vector<vector<string> > &res, vector<string> &tmpres,
string &start, string &end)
{
if(start == end)
{
reverse(tmpres.begin(), tmpres.end());
res.push_back(tmpres);
reverse(tmpres.begin(), tmpres.end());
return;
}
vector<string> &pre = prePath[end];
for(int i = ; i < pre.size(); i++)
{
tmpres.push_back(pre[i]);
ConstructResult(prePath, res, tmpres, start, pre[i]);
tmpres.pop_back();
}
}
};
另外这一题如果不用队列来进行bfs,可能会更加方便,使用两个哈希表来模拟队列,这样还可以避免前面提到的同一个元素加入队列多次的问题,具体可以参考这篇博客
【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3443512.html
LeetCode:Word Ladder I II的更多相关文章
- [leetcode]Word Ladder II @ Python
[leetcode]Word Ladder II @ Python 原题地址:http://oj.leetcode.com/problems/word-ladder-ii/ 参考文献:http://b ...
- [LeetCode] Word Ladder II 词语阶梯之二
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...
- LeetCode :Word Ladder II My Solution
Word Ladder II Total Accepted: 11755 Total Submissions: 102776My Submissions Given two words (start ...
- LeetCode: Word Ladder II 解题报告
Word Ladder II Given two words (start and end), and a dictionary, find all shortest transformation s ...
- LeetCode: Word Ladder II [127]
[题目] Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) ...
- [LeetCode] Word Ladder 词语阶梯
Given two words (beginWord and endWord), and a dictionary, find the length of shortest transformatio ...
- Word Ladder I & II
Word Ladder I Given two words (start and end), and a dictionary, find the length of shortest transfo ...
- LeetCode Word Ladder 找单词变换梯
题意:给出两个单词,以及一个set集合,当中是很多的单词.unordered_set是无序的集合,也就是说找的序列也是无序的了,是C++11的标准,可能得升级你的编译器版本了.要求找出一个从start ...
- [LeetCode] Word Ladder II
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...
随机推荐
- 利用伪类:before&&:after实现图标库图标
一.实现如下效果 二.代码实现思路 图案一源码 <!DOCTYPE html> <html> <head> <meta charset="utf-8 ...
- Dog_Hybird的诞生
起因 开玩笑说“iOS搞不动了”,另外一方面iOS组的哥哥们给力,少一个我也妥妥的.又听闻web前端组来了一个不得了的人物,“老司机,带带我”这种机会不能错过,1个多月前就申请转web前端了.开始是苦 ...
- JS中的函数(二):函数参数(你可能不知道的参数传递)
前言: 函数分为有参有返回值,有参无返回值,无参无返回值,无参有返回值:那么对于无参数的函数你想使用函数的调用怎么办呢?如果你想封装一个代码,实现多种功能,但是形参大于实参或者实参大于形参又该如何?本 ...
- childViewController 小计
设置childViewcontroller Unbalanced calls to begin/end appearance transitions for 以上报错 需要添加 transitionF ...
- 11-C语言指针&一维数组&字符串
一.用指针遍历数组元素 1.最普通的遍历方式是用数组下标来遍历元素 1 // 定义一个int类型的数组 2 int a[4] = {1, 2, 3, 4}; 3 4 int i; 5 for (i = ...
- SE(homework3)_敏捷模型
今天老师上课主要和我们讲解了软件开发模型类型.既然是敏捷模型,那么什么是非敏捷模型呢?了解这里点,会更清楚什么是敏捷模想.我们所知道的非敏捷模型有瀑布模型,我们知道这是早期软件开发的经典模型,流程主要 ...
- GitHub for Windows提交失败“failed to sync this branch”
今天github for windows同步推送远端github出问题了,提交到本地没问题,远端一直推送不上去,挺棘手的,试了几个网上的方法不管用.问题如下,报这个错: failed to sync ...
- <<你的灯亮着吗?>>读书笔记
本书是美国计算机传奇人物杰拉尔德.温伯格和唐纳德.高斯所著,我在网上买到的2003年版的本书,发现本书用20则幽默的现代寓言故事,60幅精美插图,以及一系列的适当提问和建议,让我们的思考方式慢慢得以扩 ...
- 对象映射工具AutoMapper介绍
AutoMapper是用来解决对象之间映射转换的类库.对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间. 一. AutoMapper ...
- YourSQLDba 配置——修改备份路径
公司一直使用YourSQLDba做本地备份,磁带机将本地备份文件上带做异地容灾备份.近期整理.验证备份时发现本地备份目录命名五花八门 其中有历史原因,也有无规划化的缘故,看着这些五花八门的目录,越看越 ...