Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

  1. Only one letter can be changed at a time
  2. 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.

我的写法最后的结果顺序与答案不符

 class Solution {
public:
vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
bfs(start,end,dict);
v.push_back(end);
dfs(end,start,dict);
return res;
} void bfs(string start, string end, unordered_set<string> &dict){
queue<string> q;
q.push(start);
m[start]=;
min=dict.size()+;
while(!q.empty()){
string cur=q.front();
q.pop();
for(int i=;i<cur.length();i++){
for(char c='a';c<='z';c++){
if(c==cur[i])
continue;
string tmp=cur;
tmp[i]=c;
if(tmp==end){
min=m[cur]+<min?m[cur]+:min;
m[end]=min;
continue;
}
if(dict.find(tmp)!=dict.end()&&m.find(tmp)==m.end()){
q.push(tmp);
m[tmp]=m[cur]+;
}
}
}
}
}
void dfs(string start, string end, unordered_set<string> &dict){
if(start==end){
vector<string> tmp;
for(int i=v.size()-;i>=;i--){
tmp.push_back(v[i]);
}
res.push_back(tmp);
return;
}
for(int i=;i<start.length();i++){
for(char c='a';c<='z';c++){
if(c==start[i])
continue;
string cur=start;
cur[i]=c;
if(m.find(cur)!=m.end()&&m[cur]==(m[start]-)){
v.push_back(cur);
dfs(cur,end,dict);
v.pop_back();
}
}
}
}
vector<vector<string>> res;
vector<string> v;
map<string,int> m;
int min=;
};

分析转自:http://www.cnblogs.com/ShaneZhang/p/3748494.html

其实这一题很容易在脑海汇中勾勒一下DFS/BFS搜索树的大致样子。

如果选用DFS(即广义上的爆搜递归)

 void search(string &word, string &end, unordered_set<string> &dict, int level)
{
if(word == end)
return; if( level == dict.size())
return; for(int i = ; i < word.length(); i++)
{
for(int ch = 'a'; j <='z'; j++)
{
string tmp = word;
if(tmp[i] == ch)
continue;
tmp[i] = ch;
if(dict.count(tmp) > )
search(tmp, end, dict, level+);
}
}

如此,必须要遍历整棵搜索树,记录所有可能的解路径,然后比较最短的输出,重复节点很多,时间复杂度相当大。有人问可以剪枝么,答案是这里没法剪。如果把已经访问过的剪掉,那么就会出现搜索不完全的情况。

看来直接上来爆搜是不行的。效率低的不能忍。

这样看,如果将相邻的两个单词(即只差一个字母的单词)相互连在一起,这就是一个图嘛。经典的图算法,dijiska算法不就是求解最短路径的算法么。

那么就说直接邻接表建图,然后dijkstra算法求解咯,当然是可以的,边缘权值设为1就行。而且这种思路工程化,模块化思路很明显,比较不容易出错。但此种情况下时间需建图,然后再调用dijkstra,光是后者复杂度就为o(n^2),所以仍有可能超时,或者说,至少还不是最优方法。

建图后进行DFS呢。很可惜,对于一个无向有环图,DFS只能遍历节点,求最短路径什么的还是别想了。(注意,这里对图进行DFS搜索也会生成一颗搜索树,但是与上文提到的递归爆搜得到的搜索树完全不一样哦,主要是因为对图进行DFS得不到严谨的前后关系,而这是最短路径必须具备的)

好了,我们来看看一个例子

如何对这个图进行数据结构上的优化,算法上的优化是解决问题的关键。

通过观察,容易发现这个图没有边权值,也就是所用dijkstra算法显得没必要了,简单的BFS就行,呵呵,BFS是可以求这类图的最短路径的,

正如wiki所言:若所有边的长度相等,广度优先搜索算法是最佳解——亦即它找到的第一个解,距离根节点的边数目一定最少。

所以,从出发点开始,第一次"遍历"到终点时过的那条路径就是最短的路径。而且是时间复杂度为O(|V|+|E|)。时间复杂度较dijkstra小,尤其是在边没那么多的时候。

到此为止了么。当然不是,还可以优化。

回到最原始的问题,这个图够好么?它能反映问题的本质么。所谓问题的本质,有这么两点,一是具有严格的前后关系(因为要输出所有变换序列),二是图中的边数量是否过大,能够减小一些呢?

其实,一个相对完美的图应该是这样的

这个图有两个很明显的特点,一是有向图,具有鲜明的层次特性,二是边没有冗余。此图完美的描述了解的结构。

所以,我们建图也要有一定策略,也许你们会问,我是怎么想出来的。

其实,可以这样想,我们对一个单词w进行单个字母的变换,得到w1 w2 w3...,本轮的这些替换结果直接作为当前单词w的后继节点,借助BFS的思想,将这些节点保存起来,下一轮开始的时候提取将这些后继节点作为新的父节点,然后重复这样的步骤。

这里,我们需要对节点“分层”。上图很明显分为了三层。这里没有用到队列,但是思想和队列一致的。因为队列无法体现层次关系,所以建图的时候,必须设立两个数据结构,用来保存当前层和下层,交替使用这两个数据结构保存父节点和后继节点。

同时,还需要保证,当前层的所有节点必须不同于所有高层的节点。试想,如果tot下面又接了一个pot,那么由此构造的路径只会比tot的同层pot构造出的路径长。如何完成这样的任务呢?可以这样,我们把所有高层节点从字典集合中删除,然后供给当前层选取单词。这样,当前层选取的单词就不会与上层的重复了。注意,每次更新字典的时候是在当前层处理完毕之后在更新,切不可得到一个单词就更新字典。例如我们得到了dog,不能马上把dog从待字典集合中删除,否则,下次hog生成dog时在字典中找不到dog,从而导致结果不完整。简单的说,同层的节点可以重复。上图也可以把dog化成两个节点,由dot和hog分别指向。我这里为了简单就没这么画了。

最后生成的数据结构应该这样,类似邻接表

hot---> hop, tot, dot, pot, hog

dot--->dog

hog--->dog, cog

ok。至此,问题算是基本解决了,剩下的就是如何生成路径。其实很简单,对于这种“特殊”的图,我们可以直接DFS搜索,节点碰到目标单词就返回。

这就完了,不能优化了?不,还可以优化。

可以看到,在生成路径的时候,如果能够从下至上搜索的话,就可以避免那些无用的节点,比如hop pot tot这类的,大大提升效率。其实也简单,构造数据结构时,交换一下节点,如下图

dog--->dot, hog

cog--->hog

hop--->hot

tot--->hot

dot--->hot

pot--->hot

hog--->hot

说白了,构造一个反向邻接表即可。

对了,还没说整个程序的终止条件。如果找到了,把当前层搜完就退出。如果没找到,字典迟早会被清空,这时候退出就行。

说了这么多,上代码吧

 class Solution {
public:
vector<string> temp_path;
vector<vector<string>> result_path; void GeneratePath(unordered_map<string, unordered_set<string>> &path, const string &start, const string &end)
{
temp_path.push_back(start);
if(start == end)
{
vector<string> ret = temp_path;
reverse(ret.begin(),ret.end());
result_path.push_back(ret);
return;
} for(auto it = path[start].begin(); it != path[start].end(); ++it)
{
GeneratePath(path, *it, end);
temp_path.pop_back();
}
}
vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict)
{
temp_path.clear();
result_path.clear(); unordered_set<string> current_step;
unordered_set<string> next_step; unordered_map<string, unordered_set<string>> path; unordered_set<string> unvisited = dict; if(unvisited.count(start) > )
unvisited.erase(start); current_step.insert(start); while( current_step.count(end) == && unvisited.size() > )
{
for(auto pcur = current_step.begin(); pcur != current_step.end(); ++pcur)
{
string word = *pcur; for(int i = ; i < start.length(); ++i)
{
for(int j = ; j < ; j++)
{
string tmp = word;
if( tmp[i] == 'a' + j )
continue;
tmp[i] = 'a' + j;
if( unvisited.count(tmp) > )
{
next_step.insert(tmp);
path[tmp].insert(word);
}
}
}
} if(next_step.empty()) break;
for(auto it = next_step.begin() ; it != next_step.end(); ++it)
{
unvisited.erase(*it);
} current_step = next_step;
next_step.clear();
} if(current_step.count(end) > )
GeneratePath(path, end, start); return result_path;
}
};

Word Ladder II——找出两词之间最短路径的所有可能的更多相关文章

  1. [leetcode]Word Ladder II @ Python

    [leetcode]Word Ladder II @ Python 原题地址:http://oj.leetcode.com/problems/word-ladder-ii/ 参考文献:http://b ...

  2. 18. Word Ladder && Word Ladder II

    Word Ladder Given two words (start and end), and a dictionary, find the length of shortest transform ...

  3. LeetCode :Word Ladder II My Solution

    Word Ladder II Total Accepted: 11755 Total Submissions: 102776My Submissions Given two words (start  ...

  4. LeetCode: Word Ladder II 解题报告

    Word Ladder II Given two words (start and end), and a dictionary, find all shortest transformation s ...

  5. leetcode 127. Word Ladder、126. Word Ladder II

    127. Word Ladder 这道题使用bfs来解决,每次将满足要求的变换单词加入队列中. wordSet用来记录当前词典中的单词,做一个单词变换生成一个新单词,都需要判断这个单词是否在词典中,不 ...

  6. 面试经典:链表中倒数第k个结点?如何从大量数据中找出高频词?

    记录两道面试题: 题目描述: 输入一个链表,输出该链表中倒数第k个结点.(单向链表) 拿到这个问题的时候自然而然会想到让链表从末尾开始next   K-1 次不就是第K-1个节点了么,但是必须要注意一 ...

  7. 【leetcode】Word Ladder II

      Word Ladder II Given two words (start and end), and a dictionary, find all shortest transformation ...

  8. Java - Collection 高效的找出两个List中的不同元素

    如题:有List<String> list1和List<String> list2,两个集合各有上万个元素,怎样取出两个集合中不同的元素? 方法1:遍历两个集合 public ...

  9. python——快速找出两个电子表中数据的差异

    最近刚接触python,找点小任务来练练手,希望自己在实践中不断的锻炼自己解决问题的能力. 公司里会有这样的场景:有一张电子表格的内容由两三个部门或者更多的部门用到,这些员工会在维护这些表格中不定期的 ...

随机推荐

  1. 缓存淘汰算法之FIFO

    前段时间去网易面试,被这个问题卡住,先做总结如下: 常用缓存淘汰算法 FIFO类:First In First Out,先进先出.判断被存储的时间,离目前最远的数据优先被淘汰. LRU类:Least ...

  2. javascript是脚本语言?javascript万物皆对象?

    呵呵哒!带你见识下js面对对象的魅力 是的是的,退后,朕要开始装逼了- 这是什么鸟东西?是的是的,装逼开始,2016年度最佳JS编译器,ES6标准出来后,小伙伴们对新特性摩拳擦掌,奈何浏览器支持把我们 ...

  3. 安装adb工包

    下载android sdk (很大) 从D:\AndroidSdk\platform-tools目录可以看到: 将adb工具包: adb.exe,AdbWinapi.dll,AdbWinUSBapi. ...

  4. 九度oj 题目1085:求root(N, k) 清华2010年机试题目

    题目描述: N<k时,root(N,k) = N,否则,root(N,k) = root(N',k).N'为N的k进制表示的各位数字之和.输入x,y,k,输出root(x^y,k)的值 (这里^ ...

  5. 【转】[重构]Primitive Obsession

    http://blog.csdn.net/wxr0323/article/details/7913950 Primitive Obsession(基本类型偏执) 偏执这个词实在是有点难懂.百度百科传送 ...

  6. iOS开发 UILabel实现自适应高宽

    UILabel是iOS开发常用的控件.UILabel的属性需要了解,UILabel的特殊显示效果也需要我们掌握.UILabel自适应高宽度是很多初学者遇到的技术性难题.比如段文字,要让他完全地分行显示 ...

  7. iOS学习笔记25-录音和网络流媒体

    .录音 AVFoundation框架还有一个AVAudioRecorder类专门处理录音操作,它同样支持多种音频格式. 先来了解下AVAudioRecorder的常用属性: @property (re ...

  8. 【Luogu】P4208最小生成树计数(状压乱搞)

    题目链接 最小生成树有两个性质,两个性质都知道的话这题就变成码农题了. 1.无论最小生成树长什么样,所有权值的边的数量是不变的.比如我有棵最小生成树有两条权值为2的边四条权值为1的边,那这个图的所有最 ...

  9. (C++一本通)最少转弯问题 (经典搜索)

    题目描述 给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山.平地可以通过,高山则不能.现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能 ...

  10. Bootstrap 模态框(Modal)插件数据传值

    原文:http://blog.csdn.net/baalhuo/article/details/51178154 模态框具体代码如下: <!-- 模态框(Modal) --> <di ...