zoukankan      html  css  js  c++  java
  • 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、层次搜都过了一遍了,挺不错~

  • 相关阅读:
    记录一则enq: TX
    RAC节点两边存储名字不一致导致的故障及相关延伸
    记录一则fsck的简单案例
    RAC某节点v$asm_disk查询hang分析处理
    统计信息自动收集任务失效原因排查
    OEMCC 13.2 安装部署
    Linux平台Oracle 12.1.0.2 单实例安装部署
    java 除法运算只保留整数位的4种方式
    eclipse spring 配置文件xml校验时,xsd报错
    oracle decode()函数的参数原来可以为sql语句!
  • 原文地址:https://www.cnblogs.com/flowerkzj/p/3732861.html
Copyright © 2011-2022 走看看