Leetcode#126 Word Ladder II
既然是求最短路径,可以考虑动归或广搜。这道题对字典直接进行动归是不现实的,因为字典里的单词非常多。只能选择广搜了。
思路也非常直观,从start或end开始,不断加入所有可到达的单词,直到最终到达另一端。本质上广度优先遍历图。
需要注意的是,拓展下一个单词时不能对字典进行枚举,因为字典里的单词太多。幸好单词本身都不长,所以直接枚举单词所有可能的变形,看看在dict中出现没有。
当然还不止这些,上面的做法仍然会超时,需要添加剪枝策略:
1. 如果某个单词在以前遍历过了,那么以后都不用再考虑,因为之后遍历到的路径一定不是最短的
2. 在广搜法拓展下一轮单词时,注意去重
此外还需要注意的是,不能把每个单词到start或end的路径都保存下来,那样内存会爆掉。所以要压缩保存结果,通常的做法是用一个map保存当前单词下一步是什么单词。例如next[word] = {next_word1, next_word2, next_word3...}。最后从next[start]开始再次使用广度优先搜索法构造出所有解。
算法不难,但是编码非常容易出错,所以总体上还是挺难的。最后运行时间640ms,还是有挺大优化空间的。
代码写的有些啰嗦,DFS不一定要用队列(我还是用了队列),这道题用unordered_set更好,不需要用额外的数据结构去重了。
代码:
1 bool adjacentp(string &a, string &b) { 2 for (int i = a.length() - 1, d = 0; i >= 0; i--) { 3 d += a[i] != b[i] ? 1 : 0; 4 if (d > 1) 5 return false; 6 } 7 return true; 8 } 9 10 vector<vector<string> > findLadders(string start, string end, unordered_set<string> &dict) { 11 map<string, set<string> > next; 12 unordered_set<string> covered; // 当前已经访问过的单词 13 queue<string> que; 14 bool found = false; // 是否已经找到 15 16 next[end] = set<string>(); 17 covered.insert(end); 18 que.push(end); 19 while (!que.empty() && !found) { 20 unordered_set<string> rset; 21 queue<string> rque; 22 23 while (!que.empty()) { 24 string curr = que.front(); 25 que.pop(); 26 27 if (adjacentp(curr, start)) { 28 found = true; 29 next[start].insert(curr); 30 continue; 31 } 32 33 for (int i = curr.length() - 1; i >= 0; i--) { 34 for (int j = 0; j < 26; j++) { 35 string prev = curr; 36 prev[i] = 'a' + j; 37 // 如果prev之前没有被访问过,且字典里有这个单词 38 if (covered.find(prev) == covered.end() && dict.find(prev) != dict.end()) { 39 next[prev].insert(curr); 40 // 如果在DFS的本轮拓展中还没有访问过该节点,则加入下一轮的拓展节点中 41 if (rset.find(prev) == rset.end()) { 42 rset.insert(prev); 43 rque.push(prev); 44 } 45 } 46 } 47 } 48 } 49 que = rque; 50 for (auto w : rset) { 51 covered.insert(w); 52 } 53 } 54 55 queue<vector<string> > laddersQue; 56 vector<vector<string> > ladders; 57 laddersQue.push(vector<string>(1, start)); 58 while (!laddersQue.empty()) { 59 vector<string> ladder = laddersQue.front(); 60 laddersQue.pop(); 61 if (ladder.back() == end) 62 ladders.push_back(ladder); 63 else { 64 for (auto s : next[ladder.back()]) { 65 vector<string> newLadder = ladder; 66 newLadder.push_back(s); 67 laddersQue.push(newLadder); 68 } 69 } 70 } 71 72 return ladders; 73 }