word ladder - shortest path

12 篇文章 0 订阅
2 篇文章 0 订阅

单词变换距离 Word Ladder (图的最短路径) 

  832人阅读  评论(0)  收藏  举报
  分类:
 
     
问题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.

思路:寻找邻接点的方法有两个:一个是遍历字典集合,分别判断是否是邻接的。另一个是根据字母表直接构造邻接的元素,然后判断其是否在字典集合中。当字典中数据个数较小时选第一个;当字典中数据个数多时选第二个。

    这是一个无权图的路径寻找问题。可以用DFS、也可以用BFS。虽然都可以用,但是实际上,DFS和BFS在求解的时间复杂度上差别巨大。

BFS每一层扫描都把所有能直接到达的字符串拿到,并且一定会在最近的层数被扫描到。所以BFS能更快的找到最短路径,并且遍历的次数不会太多。而DFS则不适合寻找最短路径。所以无权图的最短路径问题优先选择BFS。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class Solution {  
  2. public:  
  3. int ladderLength(string start, string end, unordered_set<string> &dict) {  
  4.       
  5.     queue<string> que;  
  6.     queue<int> level;//用来记录当前所在的层次  
  7.     int cur = 2;  
  8.     level.push(cur);  
  9.     que.push(start);  
  10.     dict.insert(end);  
  11.       
  12.     while(!que.empty())  
  13.     {  
  14.         string now = que.front();  
  15.         que.pop();  
  16.         cur = level.front();  
  17.         level.pop();  
  18.           
  19.         for(int i=0;i<start.length();i++)  
  20.         {  
  21.             for(int j=0;j<26;j++)  
  22.             {  
  23.                 string next = now;  
  24.                 next[i] = 'a' + j;  
  25.                 if(now != next && dict.find(next) != dict.end())  
  26.                 {  
  27.                     if(next == end)  
  28.                         return cur;  
  29.                     que.push(next);  
  30.                     level.push(cur+1);  
  31.                     dict.erase(next);  
  32.                 }  
  33.             }  
  34.         }  
  35.     }  
  36.     return 0;  
  37. }  
  38.   
  39. };


  40. 这一问题是《单词变换距离 Word Ladder (无权图的最短路径)》的引申问题。

    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"]
      ]
    

    思路:上一问题只要求得需要多少次变换能够从A单词到达B单词,当时使用的是BFS算法求得最短路径。现在的问题是,要求得所有可能的最短路径。因此,问题的难点在于,如何在BFS的过程当中记录路径。

        BFS是层序遍历的,没办法直接去记录深度路径。因此,要把问题稍微转化一下,在BFS的过程中,记录每个单词在层序遍历树中的父节点,也就是在最短路径中前驱结点。当遇到目标单词end时,之后层次的遍历就不再进行了(当前所在层次一定要遍历完,因为同一层次可能有多种的最短路径)。

        得到了这些结点的前驱结点(没有求得前驱结点的字符串一定不在最短路径上)之后,我从目标节点end反向查找前驱,就一定能找到出发结点start,并且这一过程中遇到的所有分支都是有效的路径

    [cpp]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. class Solution {  
    2. public:  
    3.   
    4. //宽度优先遍历:寻找最短路径,同时记录前驱单词  
    5. int bfs(string start, string end, unordered_set<string> &dict, map<string, unordered_set<string>> &prepath)  
    6. {  
    7.     dict.erase(start);  
    8.     dict.insert(end);  
    9.     int stop = 0;  
    10.     int cur = 1;  
    11.   
    12.     set<string> nextlevel;   
    13.     queue<string> que;    
    14.     queue<int> level; //和que一直伴随着,记录当前的层次。  
    15.   
    16.     level.push(cur);  
    17.     que.push(start);  
    18.   
    19.     vector<string> del;  
    20.     while(!que.empty())    
    21.     {    
    22.         string now = que.front(); // get string  
    23.         que.pop();  
    24.   
    25.         if(cur != level.front())  
    26.         {  
    27.             nextlevel.clear();  
    28.             for(int i=0;i<del.size();i++) //新层次开始时,才会把上一次用过的单词删掉  
    29.                 dict.erase(del[i]);  
    30.             del.clear();  
    31.         }  
    32.         cur = level.front(); //get level  
    33.         level.pop();  
    34.           
    35.         if(stop != 0 && stop != cur) //剪枝:若找到end,则下一层不再看  
    36.             return stop;  
    37.   
    38.         for(int i=0;i<start.length();i++)  {   
    39.             for(int j=0;j<26;j++)    
    40.             {    
    41.                 string next = now;  next[i] = 'a' + j;  
    42.                 if(now != next && dict.find(next) != dict.end())    
    43.                 {  
    44.                     prepath[next].insert(now); // Remember back path  
    45.   
    46.                     if(next == end)   
    47.                         stop = cur;  
    48.                       
    49.                     del.push_back(next);  
    50.                     if(nextlevel.find(next) == nextlevel.end()) //优化:下一层中不重复记录  
    51.                     {  
    52.                         que.push(next);  
    53.                         level.push(cur+1);   
    54.                     }  
    55.                     nextlevel.insert(next);  
    56.                 }  
    57.             }    
    58.         }  
    59.     }  
    60.     return 0;  
    61. }  
    62. //由前驱单词backtrack生成所有路径  
    63. void backtrack(string now, vector<string> &path, string start, vector<vector<string>> &re, map<string, unordered_set<string>> &prepath)  
    64. {  
    65.     if(now == start)  
    66.     {  
    67.         reverse(path.begin(), path.end());  
    68.         re.push_back(path);  
    69.         reverse(path.begin(), path.end());  
    70.         return;  
    71.     }  
    72.     unordered_set<string> s = prepath[now];  
    73.     unordered_set<string>::iterator it = s.begin();  
    74.     for(;it!=s.end();it++)  
    75.     {  
    76.         path.push_back(*it);  
    77.         backtrack(*it, path, start, re, prepath);  
    78.         path.pop_back();  
    79.     }  
    80. }  
    81. vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {  
    82.   
    83.     vector<vector<string>> re;  
    84.     map<string, unordered_set<string>> prepath;  
    85.     //首先bfs  
    86.     int level = bfs(start, end, dict, prepath);  
    87.     if(level == 0)  
    88.         return re;  
    89.           
    90.     //其次backtrack  
    91.     vector<string> path;  
    92.     path.push_back(end);  
    93.     backtrack(end, path, start, re, prepath);  
    94.     return re;  
    95. }  
    96.   
    97. };  

      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值