zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    git基本操作及设置
    5-13 多页面打包配置
    笔记待整理
    单例模式在多线程下的多种实现模式
    面试题小练习1106
    求两个字符串的最大共有子串
    单例模式
    静态初始化一个二维数组并将二维数组排序并输出
    java中数组的基本知识
    关于break语句如何结束多重循环的嵌套
  • 原文地址:https://www.cnblogs.com/boring09/p/4238381.html
Copyright © 2011-2022 走看看