Leetcode OJ: Word Ladder I/II

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence 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"]

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.

最短距离,直接用BFS暴力解决,直接就过了。比较简单,直接看代码吧:

 

 1 class Solution {
 2 public:
 3     int ladderLength(string start, string end, unordered_set<string> &dict) {
 4         unordered_map<string, string> from;
 5         queue<string> q;
 6         q.push(start);
 7         while (!q.empty()) {
 8             string cur(q.front());
 9             q.pop();;
10             for (int i = 0; i < cur.size(); ++i) {
11                 string tmp = cur;
12                 for (char c = 'a'; c <= 'z'; ++c) {
13                     if (c != tmp[i]) {
14                         cur[i] = c;
15                         if (dict.count(cur) == 0 && cur != end)
16                             continue;
17                         if (cur == end) {
18                             int count = 2;
19                             cur = tmp;
20                             while (cur != start) {
21                                 cur = from[cur];
22                                 ++count;
23                             }
24                             return count;
25                         }
26                         if (from.count(cur) == 0) {
27                             from[cur] = tmp;
28                             q.push(cur);
29                         }
30                     }
31                 }
32                 cur[i] = tmp[i];
33             }
34         }
35         
36         return 0;
37     }
38 };
View Code

760ms过了大数据。

然后就是看重头戏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.

Word Ladder II与I的区别是,一个是只需要求长度,一个是要求出所有的路径。如何保证能找到所有的路径呢?

假设我们知道最短路径是k,那么到达第k步的节点只可能是深度是k-1的节点,虽然这话很废,但很关键,这一点就要求我们要知道层次信息。

于是,我们就用一个hash表要存已经访问过的节点的层次,还可以用这个表要判断节点是否访问过,当出现我们的目的为止。

接着就是回溯了,回溯的根据还是层次,从end一直往回搜,只找层次相邻的,而变化只有一个字母的节点,直到层次为0。

看代码:

 1 class Solution {
 2 public:
 3     // 递归实现的查找路径
 4     void findLaddersHelper(string start, string end, unordered_map<string, int>& depths,
 5                             vector<vector<string> >& ret, vector<string>& item) {
 6         if (start == end) {
 7             // 把路径翻转一下
 8             reverse(item.begin(), item.end());
 9             ret.push_back(item);
10             reverse(item.begin(), item.end());
11             return;
12         }
13         int curlev = depths[end];
14         for (int i = 0; i < end.size(); ++i) {
15             string tmp(end);
16             for (char c = 'a'; c <= 'z'; ++c) {
17                 if (c == end[i])
18                     continue;
19                 tmp[i] = c;
20                 if (depths.count(tmp) == 1 &&
21                     depths[tmp] == curlev - 1) {
22                     item.push_back(tmp);
23                     findLaddersHelper(start, tmp, depths, ret, item);
24                     item.pop_back();
25                 }
26             }
27         }
28     }
29     vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
30         queue<string> q;
31         unordered_map<string, int> depths;
32         depths[start] = 0;
33         q.push(start);
34         vector<vector<string> > ret;
35         // 记录一层的长度
36         int lastcount = 1;
37         int level = 0;
38         while (lastcount > 0) {
39             // 记录新增层的长度
40             int curcount = 0;
41             while (lastcount--) {
42                 string cur(q.front());
43                 q.pop();
44                 
45                 for (int i = 0; i < cur.size(); ++i) {
46                     string tmp(cur);
47                     for (char c = 'a'; c <= 'z'; ++c) {
48                         if (c == cur[i])
49                             continue;
50                         tmp[i] = c;
51                         if (depths.count(tmp) == 0 && (dict.count(tmp) == 1 || tmp == end)) {
52                             q.push(tmp);
53                             ++curcount;
54                             depths[tmp] = level + 1;
55                         }
56                     }
57                 }
58             }
59             if (depths.count(end)) { // 一层结束后,判断是否出现end了
60                 vector<string> item(1, end);
61                 findLaddersHelper(start, end, depths, ret, item);
62                 return ret;
63             }
64             ++level;
65             lastcount = curcount;
66         }
67         return ret;
68     }
69 };

1200ms过的大数据

这应该算是leetcode上少有的耗时如此之长的题了吧。

最近也拜读了下JULY的程序员编程艺术系列,刚好看到一章讲这题,然后说用双向BFS会好不少,其实也是可以想像的,因为广度优先的话层数越深,广度就越大,而目标只有一个,所以双向至少是去除了一半的计算量,参考JULY书中讲的思路,再结合我原有的单向BFS的代码,于是就有了我自己的双向BFS了。

这里主要注意的是两个问题:

1. 查找的终止条件

2. 如何找出路径

怎么解决?看代码吧~

  1 class Solution {
  2 public:
  3     vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
  4         // 用于存状态的一些变量
  5         queue<string> start_q, end_q;
  6         unordered_map<string, int> depth_from_start, depth_from_end;
  7         unordered_set<string> meet;
  8         depth_from_start[start] = 0;
  9         depth_from_end[end] = 0;
 10         int level_from_start = 0, level_from_end = 0, count_from_start = 1, count_from_end = 1;
 11         start_q.push(start);
 12         end_q.push(end);
 13         
 14         vector<vector<string> > ret;
 15         
 16         while (count_from_start > 0 && count_from_end > 0) {
 17             if (count_from_end < count_from_start) {
 18                 count_from_end = nextLevel(end_q, count_from_end, level_from_end, end,
 19                         depth_from_end, depth_from_start, meet, dict);
 20             } else {
 21                 count_from_start = nextLevel(start_q, count_from_start, level_from_start, start,
 22                         depth_from_start, depth_from_end, meet, dict);
 23             }
 24             // 相遇即返回结果
 25             if (!meet.empty()) {
 26                 for (auto mid : meet) {
 27                     vector<string> item(1, mid);
 28                     ret.push_back(item);
 29                 }
 30                 // 找前向的所有路径
 31                 findLaddersHelper(depth_from_start, ret);
 32                 // 翻转
 33                 for (int i = 0; i < ret.size(); ++i) {
 34                     reverse(ret[i].begin(), ret[i].end());
 35                 }
 36                 // 结合找后向的所有路径
 37                 findLaddersHelper(depth_from_end, ret);
 38                 return ret;
 39             }
 40         }
 41         
 42         return ret;
 43     }
 44 private:
 45     // 对q添加下一层的节点,返回新添加的节点数,并且层数加1
 46     int nextLevel(queue<string>& q, int qcount, int& level, string& end,
 47             unordered_map<string, int>& depth, unordered_map<string, int>& other_depth,
 48             unordered_set<string>& meet, unordered_set<string>& dict) {
 49         int count = 0;
 50         while (qcount--) {
 51             string tmp = q.front();
 52             q.pop();
 53             for (int i = 0; i < tmp.size(); ++i) {
 54                 char back = tmp[i];
 55                 for (char c = 'a'; c <= 'z'; ++c) {
 56                     if (c == back)
 57                         continue;
 58                     tmp[i] = c;
 59                     if (depth.count(tmp) == 0 && (dict.count(tmp) == 1 || tmp == end)) {
 60                         q.push(tmp);
 61                         depth[tmp] = level + 1;
 62                         ++count;
 63                         // 判断是否与另一个map相交了
 64                         if (other_depth.count(tmp)) {
 65                             meet.insert(tmp);
 66                         }
 67                     }
 68                 }
 69                 tmp[i] = back;
 70             }
 71         }
 72         ++level;
 73         return count;
 74     }
 75     // 非递归实现的回溯路径,参考JULY的程序员编程艺术
 76     void findLaddersHelper(unordered_map<string, int>& depth, vector<vector<string> >& ret) {
 77         vector<vector<string> > temp;
 78         while (depth[ret.back().back()] != 0) {
 79             ret.swap(temp);
 80             ret.clear();
 81             for (int i = 0; i < temp.size(); ++i) {
 82                 string back = temp[i].back();
 83                 int curlev = depth[back];
 84                 for (int j = 0; j < back.size(); ++j) {
 85                     char b = back[j];
 86                     for (char c = 'a'; c <= 'z'; ++c) {
 87                         if (c == b)
 88                             continue;
 89                         back[j] = c;
 90                         if (depth.count(back) && depth[back] == curlev - 1) {
 91                             temp[i].push_back(back);
 92                             ret.push_back(temp[i]);
 93                             temp[i].pop_back();
 94                         }
 95                     }
 96                     back[j] = b;
 97                 }
 98             }
 99         }
100     }
101 };

最终是244ms过了,这提升还是很大的!

这样一下子就把BFS、双BFS、层次搜都过了一遍了,挺不错~

posted @ 2014-05-16 19:54  flowerkzj  阅读(335)  评论(0编辑  收藏  举报