图解LeetCode:Word Ladder

待我称王封你为后i 2023-06-19 13:26 91阅读 0赞

文章目录

        • 思考过程
        • 建立索引
        • 代码实现
        • 总结

这是LeetCode的第127号题目Word Ladder,也就是单词梯子,那这个题目是什么意思呢?

给定两个单词,一个为开头单词beginWord,一个为结尾单词endWord,并且给定一个单词列表,找出从开头单词到结尾单词的最短转换路径,转换规则是这样的:

  1. 一次只能转换单词中的一个字符
  2. 每次转换后的单词必须存在于单词列表中

比如给定开头单词“hit”,结尾单词“cog”,单词列表[“hot”,“dot”,“dog”,“lot”,“log”,“cog”],那么最短的转换路径是这样的:“hit” -> “hot” -> “dot” -> “dog” -> “cog”,因此应该返回5,如果不存在这样的一个转换路径那么返回0。

这个问题该如何解决呢,你有思路吗?

思考过程

这个问题本质上是一个搜索问题,从开头单词到结尾单词可能存在多条转换路径,我们需要将最短的那个找出来,因此我们需要将所有的可能路径都计算出来,问题是该怎么计算呢?

很简单,如果你一下想不到该怎么找出一条可能的路径,那么我们就从简单的第一步开始,我们还是以本文开始示例来讲解,hit下一步转换后的单词一定出自单词列表(如果没有的话那么说明根本不存在这样一条转换路径,直接返回0就可以了),如图所示:

1574766791602

但是,根据题目要求hit的下一步不能是dot、dog、lot、log以及cog,因为题目要求一次只能转换一个字符,因此从hit开始的下一步只能是hot,如图所以:

1574759446030

这样我们就来到了hot,同样的道理我们知道hot的下一步有两种可能,分别是dot和lot,如图所示:

1574759501927

这样我们就来到了第三步,现在你应该明白了吧,从dot和lot开始继续上述过程,最终你能找到一条到达结尾单词的路径。

有的同学可能已经看出问题了,虽然这个方法可行,但是每次都要查一遍单词列表排除掉不符合题目要求的单词,这个过程非常耗时,该如何改进呢?

建立索引

聪明的你应该想到了吧,为什么我们不提前计算出各个单词所有满足要求的转换单词呢,这样我们就不用在搜索过程中再去遍历单词列表了,实际上提前进行预处理也就是建立一个简单的索引,索引从key是各个单词,value是该key对应的所有可能的转换单词:

  1. hot : dot,lot
  2. dot : hot,dog,lot
  3. dog : dot,log,cog
  4. lot : hot,dot,log
  5. log : dog,lot,cog
  6. cog : dog,log

这样上述从hit到hot以及到{dot、lot}的搜索过程就如图所示了:

1574759554739

接下来我们来到了dot节点,通过提前建立的索引我们知道,dot的所有可能的下一转换单词是:hot,dog,lot,但是不要忘了其中的hot和lot是已经出现在之前的搜索路径中了,因此我们需要排除掉hot和lot,如图所示:

1574759594357

接着让我们处理lot,从索引中知道lot的下一个转换单词是hot、dot、log,基于同样的道理我们应该排除掉hot和dot,因为这两个单词已经出现在搜索路径中了,这样我们得到了下图:

1574759631841

接下来我们来到了dog,从索引中知道dog的下一个转换单词是dot、log、cog,其中dot和log应该排除掉,同样因为之前已经出现过了,然后就是cog,Bingo,cog就是我们要找的结尾单词,如图所示:

1574759660313

就这样我们找到了一条可行的路径,而且这条路径就是最短的,这是很显然的,因为我们是按照层的顺序来搜索的,因此只要能找到一条路径,那么就一定是最短的,这个最短路径就是”hit” -> “hot” -> “dot” -> “dog” -> “cog”,如图所示:

1574759701629

这样问题就解决啦,有了上述分析,就可以写代码实现啦。

代码实现

  1. bool isM(string& a,string& b){
  2. int len = a.size();
  3. int num = 0;
  4. for(int i=0;i<len;i++)
  5. if (a[i]!=b[i])
  6. if ((++num) > 1)
  7. return false;
  8. return true;
  9. }
  10. int searchladder(string b, string e, map<string, bool>&p,
  11. map<string, vector<string>>& mv) {
  12. vector<string>q;
  13. vector<string>tq;
  14. q.push_back(b);
  15. p[b]=true;
  16. int res = 0;
  17. while(!q.empty()){
  18. ++res;
  19. for(int i=0;i<q.size();i++){
  20. vector<string>& adj=mv[q[i]];
  21. for(int j=0;j<adj.size();j++){
  22. if (adj[j] == e){
  23. return res+1;
  24. }
  25. if (!p[adj[j]]) {
  26. p[adj[j]]=true;
  27. tq.push_back(adj[j]);
  28. }
  29. }
  30. }
  31. swap(q,tq);
  32. tq.clear();
  33. }
  34. return 0;
  35. }
  36. int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
  37. bool is_e = false;
  38. int len = wordList.size();
  39. for(int i=0;i<len;i++){
  40. if (endWord == wordList[i]){
  41. is_e = true;
  42. break;
  43. }
  44. }
  45. if (!is_e)
  46. return 0;
  47. map<string, bool>p;
  48. map<string, vector<string>> mv;
  49. for(int i=0;i<len;i++) {
  50. for(int j=0;j<len;j++)
  51. if (i!=j && isM(wordList[i], wordList[j])){
  52. mv[wordList[i]].push_back(wordList[j]);
  53. }
  54. if (isM(beginWord, wordList[i])){
  55. mv[beginWord].push_back(wordList[i]);
  56. }
  57. }
  58. return searchladder(beginWord, endWord, p,mv);
  59. }

总结

这个题目思路上还是很直接的,关键一点在于预处理,也就是提前将每个单词所有可能的下一步转换计算出来,这将大大减少问题的搜索规模,因此这个题目的难点在于优化,实际上上述建立索引的过程还不是最优的,你能对其进一步优化吗?

更多计算机内功文章,欢迎关注微信公共账号:码农的荒岛求生

在这里插入图片描述
彻底理解操作系统系列文章
1,什么程序?
2,进程?程序?傻傻分不清
3,程序员应如何理解内存:上篇
4,程序员应如何理解内存:下篇

计算机内功决定程序员职业生涯高度

发表评论

表情:
评论列表 (有 0 条评论,91人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Word Ladder--LeetCode

    这道题看似一个关于字符串操作的题目,其实要解决这个问题得用图的方法。我们先给题目进行图的映射,顶点则是每个字符串,然后两个字符串如果相差一个字符则我们进行连边。接下来看看这个方