[LeetCode] Word Ladder II

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)
  1 class Solution {
  2     public:
  3     vector<vector<string> > findLadders(string start, string end, unordered_set<string>& dict)
  4     {
  5         vector<vector<string> > result;
  6 
  7         if(start.empty() || end.empty() )
  8             return result;
  9         if(start.size() != end.size())
 10             return result;
 11 
 12         size_t size =  start.size();
 13 
 14         unordered_set<string> cur, next;//use set to aviod add duplicate element
 15         unordered_map<string, vector<string> > father;
 16     unordered_set<string> visited; // 判重
 17 
 18         cur.insert(start);
 19 
 20         bool found = false;
 21 
 22         while(!cur.empty() && !found)
 23         {
 24         for (const auto& word : cur)
 25             visited.insert(word);
 26             for(unordered_set<string>::iterator it = cur.begin(); it != cur.end(); it++)
 27             {
 28                string curStr= *it;
 29 
 30                //cout << "=========" <<curStr <<"========================" <<endl;
 31                for(int i = 0; i< size; i++)
 32                {
 33                    for(char j = 'a'; j <= 'z'; j++ )
 34                    {
 35                        string nextStr = curStr;
 36                        if(nextStr[i] == j)
 37                            continue;
 38                        nextStr[i] = j;
 39                        if(nextStr.compare(end) == 0)
 40                        {
 41                            //if found, just traverse this layer, not go to next layer
 42                            found = true;
 43                            father[nextStr].push_back(curStr);
 44                        }
 45 
 46 #if 0
 47                        cout << "nextStr = " << nextStr<< endl;
 48                        cout << "nextStr [i] = " << nextStr[i]<< endl;
 49                        cout << "i       = " << i<< endl;
 50                        cout << "j       = " << j<< endl;
 51 #endif
 52                        //if(dict.find(nextStr) != dict.end() && cur.find(nextStr) == cur.end())
 53                        if(dict.find(nextStr) != dict.end() && !visited.count(new_word)) 
 54                            // must add "&& cur.find(nextStr) == cur.end()"
 55                            // to avoid the same level 's elemnt point each other
 56                            // for example hot --> dot
 57                            //             hot --> lot
 58                            // but when you travel to dot, may happen dot --> lot
 59                            // but when you travel to lot, may happen lot --> dot
 60                            // so when add it to next, we must confirm that lot is not in this level
 61                        {
 62                            //cout << "insert	" << nextStr<< "	to next queue" << endl;
 63                            next.insert(nextStr);
 64                            father[nextStr].push_back(curStr);
 65                        }
 66                    }
 67                }
 68             }
 69 
 70             // remove the cur layer's elements  form dict
 71             for(unordered_set<string>::iterator it = cur.begin(); it != cur.end(); it++)
 72             {
 73                string tmp = *it;
 74                 if(dict.find(tmp) != dict.end())
 75                 {
 76                     dict.erase(dict.find(tmp));
 77                     //cout << "erase 	" << tmp <<endl;
 78                 }
 79             }
 80 
 81             cur.clear();
 82             swap(cur, next);
 83         }
 84 
 85         vector<string> path;
 86         for(unordered_map<string, vector<string> >::iterator it = father.begin(); it != father.end(); it++)
 87         {
 88             string str =(*it).first;
 89             vector<string> vect =(*it).second;
 90             //cout << "map key :" << str <<endl;
 91             for(int i = 0; i < vect.size(); i++)
 92             {
 93                 //cout << "	map value:" << vect[i]<<endl;
 94             }
 95         }
 96         genPath(start, end, path, father, result);
 97         return result;
 98     }
 99 
100     void genPath(string start, string end, vector<string>& path, unordered_map<string, vector<string> >map, vector<vector<string> >& result)
101     {
102         path.push_back(end);
103         if(start == end)
104         {
105             reverse(path.begin(), path.end());
106             result.push_back(path);
107             //printVector(path);
108             reverse(path.begin(), path.end());
109         }
110         else
111         {
112             int size = map[end].size();
113             for( int i = 0; i < size; i++)
114             {
115                 string str = map[end][i];
116                 genPath(start, str, path, map, result);
117             }
118         }
119         path.pop_back();
120     }
121 };

大数据会超时,

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

 1 class Solution {
 2     public:
 3         vector<vector<string> > findLadders(string start, string end,
 4                 const unordered_set<string> &dict) {
 5             unordered_set<string> current, next; // 当前层,下一层,用集合是为了去重
 6             unordered_set<string> visited; // 判重
 7             unordered_map<string, vector<string> > father; //
 8             bool found = false;
 9             auto state_is_target = [&](const string &s) {return s == end;};
10             auto state_extend = [&](const string &s) {
11                 unordered_set<string> result;
12                 for (size_t i = 0; i < s.size(); ++i) {
13                     string new_word(s);
14                     for (char c = 'a'; c <= 'z'; c++) {
15                         if (c == new_word[i]) continue;
16                         swap(c, new_word[i]);
17                         if ((dict.count(new_word) > 0|| new_word == end) &&
18                                 !visited.count(new_word)) {
19                             result.insert(new_word);
20                         }
21                         swap(c, new_word[i]); // 恢复该单词
22                     }
23                 }
24                 return result;
25             };
26             current.insert(start);
27             while (!current.empty() && !found) {
28                 // 先将本层全部置为已访问,防止同层之间互相指向
29                 for (const auto& word : current)
30                     visited.insert(word);
31                 for (const auto& word : current) {
32                     const auto new_states = state_extend(word);
33                     for (const auto &state : new_states) {
34                         if (state_is_target(state)) found = true;
35                         next.insert(state);
36                         father[state].push_back(word);
37                         // visited.insert(state); // 移动到最上面了
38                     }
39                 }
40                 current.clear();
41                 swap(current, next);
42             }
43             vector<vector<string> > result;
44             if (found) {
45                 vector<string> path;
46                 gen_path(father, path, start, end, result);
47             }
48             return result;
49         }
50     private:
51         void gen_path(unordered_map<string, vector<string> > &father,
52                 vector<string> &path, const string &start, const string &word,
53                 vector<vector<string> > &result) {
54             path.push_back(word);
55             if (word == start) {
56                 result.push_back(path);
57                 reverse(result.back().begin(), result.back().end());
58             } else {
59                 for (const auto& f : father[word]) {
60                     gen_path(father, path, start, f, result);
61                 }
62             }
63             path.pop_back();
64         }
65 };
原文地址:https://www.cnblogs.com/diegodu/p/3821739.html