zoukankan      html  css  js  c++  java
  • LeetCode: Word Ladder II 解题报告

    Word Ladder II

    Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

    Only one letter can be changed at a time
    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.

    SOLUTION 1:

    这题做得真累,主页君差点一口老血吐在屏幕上,感觉一不小心就会超时了。

    其实最后还是可以采用跟Word Ladder 1一样的解法。使用BFS遍历可能的解。不同的有以下几点:

    1. 分层次遍历的时候,不要把找到的解的单词直接放在MAP中,而是可以放在别的一个临时的MAP中,一层遍历完成后,再统一加到MAP。这样做的上的是:

    如果本层找到一个单词比如Word,本层的其它单词如果也可以到达Word,我们仍然要把它的解都记下来。

    2. 添加到队列的时候,也要判断是不是一个新的单词(在临时MAP中查一下),是新的才能放在Queue中,否则因为1的关系,我们会计算某个单词的所有的解,所以会一直访问它。为了避免重复加入一个单词,这一步是有必要的。

    3. 与Word Ladder1中的set不同,我们使用一个hasmap来记录路径中的单词,另外,每一个单词对应的value是一个string的list,记录到达此单词的所有的可能的路径。创建这些路径的方法是:把前节点的所有的路径加上当前单词,复制一份加过来即可。

    REF: http://blog.csdn.net/whuwangyi/article/details/21611433

     1 public class Solution {
     2     public static List<List<String>> findLadders(String start, String end, Set<String> dict) {
     3         if (start == null || end == null) {
     4             return null;
     5         }
     6         
     7         Queue<String> q = new LinkedList<String>();
     8         
     9         // 存储每一个单词对应的路径
    10         HashMap<String, List<List<String>>> map = new HashMap<String, List<List<String>>>();
    11         
    12         // 标记在某一层找到解
    13         boolean find = false;
    14         
    15         // store the length of the start string.
    16         int lenStr = start.length();
    17         
    18         List<List<String>> list = new ArrayList<List<String>>();
    19         
    20         // 唯一的路径
    21         List<String> path = new ArrayList<String>();
    22         path.add(start);
    23         list.add(path);
    24         
    25         // 将头节点放入
    26         map.put(start, list);
    27         q.offer(start);
    28         
    29         while (!q.isEmpty()) {
    30             int size = q.size();
    31             
    32             HashMap<String, List<List<String>>> mapTmp = new HashMap<String, List<List<String>>>();    
    33             for (int i = 0; i < size; i++) {
    34                 // get the current word.
    35                 String str = q.poll();
    36                 for (int j = 0; j < lenStr; j++) {
    37                     StringBuilder sb = new StringBuilder(str);
    38                     for (char c = 'a'; c <= 'z'; c++) {
    39                         sb.setCharAt(j, c);
    40                         String tmp = sb.toString();
    41                         
    42                         // 1. 重复的单词,不需要计算。因为之前一层出现过,再出现只会更长
    43                         // 2. 必须要在字典中出现
    44                         if (map.containsKey(tmp) || (!dict.contains(tmp) && !tmp.equals(end))) {
    45                             continue;
    46                         }
    47                         
    48                         // 将前节点的路径提取出来
    49                         List<List<String>> pre = map.get(str);
    50                         
    51                         // 从mapTmp中取出节点,或者是新建一个节点
    52                         List<List<String>> curList = mapTmp.get(tmp);
    53                         if (curList == null) {
    54                             // Create a new list and add to the end word.
    55                             curList = new ArrayList<List<String>>();
    56                             mapTmp.put(tmp, curList);
    57                         
    58                             // 将生成的单词放入队列,以便下一次继续变换
    59                             // 放在这里可以避免Q重复加入
    60                             q.offer(tmp);
    61                         }
    62                         
    63                         // 将PRE的path 取出,加上当前节点,并放入curList中
    64                         for(List<String> pathPre: pre) {
    65                             List<String> pathNew = new ArrayList<String>(pathPre);
    66                             pathNew.add(tmp);
    67                             curList.add(pathNew);
    68                         }
    69                         
    70                         if (tmp.equals(end)) {
    71                             find = true;
    72                         }
    73                     }
    74                 }
    75             }
    76             
    77             if (find) {
    78                 return mapTmp.get(end);
    79             }
    80             
    81             // 把当前层找到的解放在MAP中。
    82             // 使用2个map的原因是:在当前层中,我们需要把同一个单词的所有的解全部找出来.
    83             map.putAll(mapTmp);
    84         }
    85         
    86         // 返回一个空的结果
    87         return new ArrayList<List<String>>();
    88     }
    89 }

     2014.12.18 Redo:

    做了一个小小的优化,拿掉了Find Flag:

     1 public class Solution {
     2     public static List<List<String>> findLadders(String start, String end, Set<String> dict) {
     3         List<List<String>> ret = new ArrayList<List<String>>();
     4         if (start == null || end == null || dict == null) {
     5             return ret;
     6         }
     7         
     8         HashMap<String, List<List<String>>> map = new HashMap<String, List<List<String>>>();
     9         
    10         // Store the map of the current level.
    11         HashMap<String, List<List<String>>> mapTmp = new HashMap<String, List<List<String>>>();
    12         
    13         Queue<String> q = new LinkedList<String>();
    14         q.offer(start);
    15         
    16         List<List<String>> listStart = new ArrayList<List<String>>();
    17         
    18         // 唯一的路径
    19         List<String> path = new ArrayList<String>();
    20         path.add(start);
    21         listStart.add(path);
    22         
    23         // 将头节点放入
    24         map.put(start, listStart);
    25         
    26         while (!q.isEmpty()) {
    27             int size = q.size();
    28             
    29             for (int i = 0; i < size; i++) {
    30                 String s = q.poll();
    31                 
    32                 int len = s.length();
    33                 for (int j = 0; j < len; j++) {
    34                     StringBuilder sb = new StringBuilder(s);
    35                     for (char c = 'a'; c <= 'z'; c++) {
    36                         // Bug 2: should seperate the setCharAt(j, c) function and the sb.toString() function.
    37                         sb.setCharAt(j, c);
    38                         String tmp = sb.toString();
    39                         
    40                         // 1. 不在字典中,并且不是end.
    41                         // 2. 前面的map中已经出现过
    42                         
    43                         // Bug 1: map should use containsKey;
    44                         if ((!dict.contains(tmp) && !tmp.equals(end)) || map.containsKey(tmp)) {
    45                             continue;
    46                         }
    47                         
    48                         // Try to get the pre list.
    49                         List<List<String>> pre = map.get(s);
    50                         
    51                         // 从mapTmp中取出节点,或者是新建一个节点
    52                         List<List<String>> curList = mapTmp.get(tmp);
    53                         if (curList == null) {
    54                             curList = new ArrayList<List<String>>();
    55                             
    56                             // Only offer new string to the queue.
    57                             // 将生成的单词放入队列,以便下一次继续变换
    58                             // 放在这里可以避免Q重复加入
    59                             q.offer(tmp);
    60                             
    61                             // create a new map.
    62                             mapTmp.put(tmp, curList);
    63                         }
    64                         
    65                         // 将PRE的path 取出,加上当前节点,并放入curList中
    66                         for (List<String> strList: pre) {
    67                             // Should create a new list. 
    68                             List<String> strListNew = new ArrayList<String>(strList);
    69                             strListNew.add(tmp);
    70                             curList.add(strListNew);
    71                         }
    72                     }
    73                 }
    74             }
    75             
    76             if (mapTmp.containsKey(end)) {
    77                 return mapTmp.get(end);
    78             }
    79             
    80             // add the tmp map into the map.
    81             map.putAll(mapTmp);
    82         }
    83         
    84         return ret;
    85     }
    86 }
    View Code

    SOLUTION 2:

    http://www.ninechapter.com/solutions/

    GITHUB:

    https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/string/FindLadders.java

    https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/string/FindLadders_1218_2014.java

  • 相关阅读:
    用价值链分析软件开发及杂感
    《恰如其分的软件架构》笔记摘要
    观察者模式
    Js中Date对象
    Js中Currying的应用
    read命令
    模板方法模式
    两两交换链表中的节点
    享元模式
    Js中Array对象
  • 原文地址:https://www.cnblogs.com/yuzhangcmu/p/4119492.html
Copyright © 2011-2022 走看看