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.

思路转载 http://www.cnblogs.com/TenosDoIt/p/3443512.html

分析:本题主要的框架和上一题是一样,但是还要解决两个额外的问题:一、 怎样保证求得所有的最短路径;二、 怎样构造这些路径

第一问题:

  • 不能像上一题第二点注意那样,找到一个单词相邻的单词后就立马把它从字典里删除,因为当前层还有其他单词可能和该单词是相邻的,这也是一条最短路径,比如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:
vector<vector<string> > findLadders(string start, string end, unordered_set<string>& dict)
{
vector<vector<string> > result; if(start.empty() || end.empty() )
return result;
if(start.size() != end.size())
return result; size_t size = start.size(); unordered_set<string> cur, next;//use set to aviod add duplicate element
unordered_map<string, vector<string> > father;
unordered_set<string> visited; // 判重 cur.insert(start); bool found = false; while(!cur.empty() && !found)
{
for (const auto& word : cur)
visited.insert(word);
for(unordered_set<string>::iterator it = cur.begin(); it != cur.end(); it++)
{
string curStr= *it; //cout << "=========" <<curStr <<"========================" <<endl;
for(int i = ; i< size; i++)
{
for(char j = 'a'; j <= 'z'; j++ )
{
string nextStr = curStr;
if(nextStr[i] == j)
continue;
nextStr[i] = j;
if(nextStr.compare(end) == )
{
//if found, just traverse this layer, not go to next layer
found = true;
father[nextStr].push_back(curStr);
} #if 0
cout << "nextStr = " << nextStr<< endl;
cout << "nextStr [i] = " << nextStr[i]<< endl;
cout << "i = " << i<< endl;
cout << "j = " << j<< endl;
#endif
//if(dict.find(nextStr) != dict.end() && cur.find(nextStr) == cur.end())
if(dict.find(nextStr) != dict.end() && !visited.count(new_word))
// must add "&& cur.find(nextStr) == cur.end()"
// to avoid the same level 's elemnt point each other
// for example hot --> dot
// hot --> lot
// but when you travel to dot, may happen dot --> lot
// but when you travel to lot, may happen lot --> dot
// so when add it to next, we must confirm that lot is not in this level
{
//cout << "insert\t" << nextStr<< "\tto next queue" << endl;
next.insert(nextStr);
father[nextStr].push_back(curStr);
}
}
}
} // remove the cur layer's elements form dict
for(unordered_set<string>::iterator it = cur.begin(); it != cur.end(); it++)
{
string tmp = *it;
if(dict.find(tmp) != dict.end())
{
dict.erase(dict.find(tmp));
//cout << "erase \t" << tmp <<endl;
}
} cur.clear();
swap(cur, next);
} vector<string> path;
for(unordered_map<string, vector<string> >::iterator it = father.begin(); it != father.end(); it++)
{
string str =(*it).first;
vector<string> vect =(*it).second;
//cout << "map key :" << str <<endl;
for(int i = ; i < vect.size(); i++)
{
//cout << "\tmap value:" << vect[i]<<endl;
}
}
genPath(start, end, path, father, result);
return result;
} void genPath(string start, string end, vector<string>& path, unordered_map<string, vector<string> >map, vector<vector<string> >& result)
{
path.push_back(end);
if(start == end)
{
reverse(path.begin(), path.end());
result.push_back(path);
//printVector(path);
reverse(path.begin(), path.end());
}
else
{
int size = map[end].size();
for( int i = ; i < size; i++)
{
string str = map[end][i];
genPath(start, str, path, map, result);
}
}
path.pop_back();
}
};

大数据会超时,

转一个网上找的能通过的版本,随后在仔细分析差距在哪里吧。。

 class Solution {
public:
vector<vector<string> > findLadders(string start, string end,
const unordered_set<string> &dict) {
unordered_set<string> current, next; // 当前层,下一层,用集合是为了去重
unordered_set<string> visited; // 判重
unordered_map<string, vector<string> > father; // 树
bool found = false;
auto state_is_target = [&](const string &s) {return s == end;};
auto state_extend = [&](const string &s) {
unordered_set<string> result;
for (size_t i = ; i < s.size(); ++i) {
string new_word(s);
for (char c = 'a'; c <= 'z'; c++) {
if (c == new_word[i]) continue;
swap(c, new_word[i]);
if ((dict.count(new_word) > || new_word == end) &&
!visited.count(new_word)) {
result.insert(new_word);
}
swap(c, new_word[i]); // 恢复该单词
}
}
return result;
};
current.insert(start);
while (!current.empty() && !found) {
// 先将本层全部置为已访问,防止同层之间互相指向
for (const auto& word : current)
visited.insert(word);
for (const auto& word : current) {
const auto new_states = state_extend(word);
for (const auto &state : new_states) {
if (state_is_target(state)) found = true;
next.insert(state);
father[state].push_back(word);
// visited.insert(state); // 移动到最上面了
}
}
current.clear();
swap(current, next);
}
vector<vector<string> > result;
if (found) {
vector<string> path;
gen_path(father, path, start, end, result);
}
return result;
}
private:
void gen_path(unordered_map<string, vector<string> > &father,
vector<string> &path, const string &start, const string &word,
vector<vector<string> > &result) {
path.push_back(word);
if (word == start) {
result.push_back(path);
reverse(result.back().begin(), result.back().end());
} else {
for (const auto& f : father[word]) {
gen_path(father, path, start, f, result);
}
}
path.pop_back();
}
};

[LeetCode] 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. LeetCode :Word Ladder II My Solution

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

  3. LeetCode: Word Ladder II 解题报告

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

  4. [LeetCode] Word Ladder II 词语阶梯之二

    Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...

  5. LeetCode: Word Ladder II [127]

    [题目] Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) ...

  6. leetcode—word ladder II

    1.题目描述 Given two words (start and end), and a dictionary, find all shortest transformation sequence( ...

  7. LeetCode:Word Ladder I II

    其他LeetCode题目欢迎访问:LeetCode结题报告索引 LeetCode:Word Ladder Given two words (start and end), and a dictiona ...

  8. [Leetcode Week5]Word Ladder II

    Word Ladder II 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/word-ladder-ii/description/ Descripti ...

  9. 【leetcode】Word Ladder II

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

随机推荐

  1. 关于浏览器cookie的那些事儿

    昨天接到一个小需求,就是在ipad上访问某页面,页面顶部出现一个下载客户端的提示,点击关闭按钮后,提示信息消失,信息存入cookie,在cookie未过期之前,除非用户自己清除浏览器的cookie,否 ...

  2. Linux操作系统里查看所有用户

    Xwindows界面的就不说了. 1.Linux里查看所有用户 linux里,并没有像windows的net user,net localgroup这些方便的命令来管理用户. (1)在终端里.其实只需 ...

  3. [MetaHook] Load DTX texture to OpenGL

    This function load a LithTech *.dtx texture file and convert to OpenGL pixel format, compressed supp ...

  4. Sonatype Nexus高级配置

    Sonatype Nexus的安装配置参见:CentOS系统中安装Nexus并导入已有的构件库.Nexus内置了Jetty容器,${NEXUS_HOME}/bin/jsw目录下包含了各个操作系统的启动 ...

  5. Linux下硬盘安装Windows系统。

    注意:本方法安装后会把Linux系统损坏,方法适用于完全不再需要Linux系统. 本方法在ubuntu 14.04,centos 6.5,debian 8测试成功. 安装方法是通过grub2引导Win ...

  6. 关于#define预处理指令的一个问题

    背景:由于经常需要在远程服务端和测试服务端进行切换,所以将接口的地址定义为了一个预处理变量,例如 //#define APIDOMAIN @"http://10.0.0.2" #d ...

  7. 编写高质量代码改善C#程序的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]

    前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...

  8. 深入理解C#泛型

    前面两篇文章介绍了C#泛型的基本知识和特性,下面我们看看泛型是怎么工作的,了解一下泛型内部机制. 泛型内部机制 泛型拥有类型参数,通过类型参数可以提供"参数化"的类型,事实上,泛型 ...

  9. 第六章:Javascript对象

    对象是javascript的基本数据类型.对象是一种复合值.它将很多值(原始值 或者其他对象)聚合在一起.可通过名字访问这些值.对象也可以看做是属性的无序集合,每个属性都有一个名/值.属性名是字符串, ...

  10. JDK的目录

    要想深入了解Java必须对JDK的组成, 本文对JDK6里的目录做了基本的介绍,主要还是讲解 了下JDK里的各种可执行程序或工具的用途 Java(TM) 有两个平台 JRE 运行平台,包括Java虚拟 ...