LeetCode127—Word Ladder

女爷i 2022-08-22 00:28 253阅读 0赞

LeetCode127—Word Ladder

原题

Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,

Given:
beginWord = “hit”
endWord = “cog”
wordList = [“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.


分析1

要把这题转化成图论的问题,下述思路构建图:

1.beginWord、endWord、wordList里面的词作为图中的节点。
2.以确定这些节点是否可达:如果变换一个字符能够到另一个单词则说明可达
3.对图进行广度优先搜索,找出起点到终点的长度。

问题

这是我最开始的思路,并且实现了,但是遇到了一个问题,就是 当wordList中单词太多则图的规模就会变得很大,最终就会超时(Time Limit Exceeded),这里也贴出超时代码吧:

  1. class Solution {
  2. private:
  3. bool isConnect(const string &a, const string&b)
  4. {
  5. //判断两个单词是否可达关系
  6. int count = 0;
  7. for (int i = 0; i < a.size(); i++)
  8. {
  9. if (a[i] != b[i])
  10. count++;
  11. if (count > 1)
  12. return false;
  13. }
  14. if (count == 1)//只有一个字符不同
  15. return true;
  16. else
  17. return false;
  18. }
  19. void buildMap(vector<vector<int>>&wordMap, map<string,int>wordTable)
  20. {
  21. int mapSize = wordTable.size();
  22. for (auto it1 = wordTable.begin(); it1 != wordTable.end(); it1++)
  23. {
  24. for (auto it2 = it1; it2 != wordTable.end(); it2++)
  25. {
  26. if (isConnect(it1->first, it2->first))
  27. {
  28. //初始化邻接矩阵(1-可达 0-不可达)
  29. wordMap[it1->second][it2->second] = 1;
  30. wordMap[it2->second][it1->second] = 1;
  31. }
  32. }
  33. }
  34. }
  35. int BFS(vector<vector<int>>&wordMap, string start, string end, map<string, int>wordTable)
  36. {
  37. vector<bool>visit(wordTable.size(), false);//访问表
  38. queue<int>q;
  39. queue<int>d;//记录距离distance的辅助队列
  40. q.push(wordTable[start]);
  41. d.push(1);
  42. visit[wordTable[start]] = true;
  43. while (!q.empty())
  44. {
  45. int t = d.front();
  46. int i = q.front();
  47. d.pop();
  48. q.pop();
  49. if (i == wordTable[end])//找到终点
  50. {
  51. return t;
  52. }
  53. for (int j = 0; j < wordMap.size(); j++)
  54. {
  55. if (!visit[j] && wordMap[i][j] == 1)
  56. {
  57. visit[j] = true;
  58. q.push(j);
  59. d.push(t + 1);
  60. }
  61. }
  62. }//end while
  63. return 0;
  64. }
  65. public:
  66. int ladderLength(string beginWord, string endWord, unordered_set<string>& wordList) {
  67. map<string, int>wordTable;//单词索引表
  68. wordTable[beginWord] = 0;
  69. wordTable[endWord] = 1;
  70. auto it = wordList.begin();
  71. int index = 2;
  72. for (; it != wordList.end(); it++)//
  73. {
  74. if (wordTable.find(*it)==wordTable.end())
  75. wordTable[*it] = index++;//建立一个单词-索引表方便建图
  76. }
  77. int size = wordTable.size();
  78. vector<vector<int>> wordMap(size,vector<int>(size,0));//初始化邻接矩阵
  79. buildMap(wordMap, wordTable);//建图
  80. return BFS(wordMap, beginWord, endWord, wordTable);
  81. //return 0;
  82. }
  83. };

分析2

其实不用把整个图建出来,可以走一步看一步,也就是说:

当访问到节点A时,可以先求出所有跟节点A临接且未被访问的节点入队,然后按照BFS的思路做即可。

但是,修改后的代码依然是超时,后来找原因发现是我在“判断两个节点是否临接”这个步骤中使用的算法效率太低,对比网上的思路:

我的方法是:把当前节点的单词current和wordList里面的所有词比较,不相同的字符的个数为1的时候return true,否则return false。这样做的缺点是,不论如何都要遍历完整个wordList,查找时间复杂度是O(n)。

网上一种思路是:替换当前节点的单词current每个字符(从a~z),然后看看替换过后的单词是否在单词表中,这里我不理解的是,由于我们是用unordered_set来保存单词表,虽然最坏情况下的时间复杂度会到O(n),但一般情况下是可以在常数时间O(1)下访问的,因此使用unordered_set::find的时间复杂度是要低于线性时间复杂度的,这样就提高了效率。


代码

  1. class Solution {
  2. private:
  3. void buildMap(string end,vector<string>&connect,unordered_set<string>&visit,string& current,const unordered_set<string>&wordList)
  4. {
  5. connect.clear();
  6. string cur = current;
  7. /*
  8. 超时:时间复杂度O(n)
  9. for(auto i=wordList.begin();i!=wordList.end();i++)
  10. {
  11. if(visit.find(*i)!=visit.end())
  12. continue;
  13. if(isConnect(cur,*i))
  14. connect.push_back(*i);
  15. }
  16. */
  17. #if 1
  18. for (int i = 0; i < cur.size(); i++)
  19. {
  20. char t = cur[i];
  21. for (char c = 'a'; c < 'z'; c++)
  22. {
  23. if (c == t)
  24. {
  25. continue;
  26. }
  27. cur[i] = c;
  28. if ((cur == end || wordList.find(cur) != wordList.end()) && (visit.find(cur) == visit.end()))
  29. {
  30. connect.push_back(cur);
  31. }
  32. }
  33. cur[i]=t;
  34. }
  35. #endif
  36. }
  37. int BFS(string beginWord, string endWord, unordered_set<string>& wordList)
  38. {
  39. queue<string>q;
  40. queue<int>d;//路径distance的辅助队列
  41. unordered_set<string>visit;
  42. vector<string>connect;
  43. q.push(beginWord);
  44. d.push(1);
  45. while (!q.empty())
  46. {
  47. string current = q.front();
  48. int tmpDist = d.front();
  49. q.pop();
  50. d.pop();
  51. buildMap(endWord,connect, visit, current, wordList);//获取临接单词
  52. if (current == endWord)//找到终点
  53. {
  54. return tmpDist;
  55. }
  56. for (int i = 0; i < connect.size(); i++)
  57. {
  58. if (visit.find(connect[i]) == visit.end())//未被访问
  59. {
  60. visit.insert(connect[i]);
  61. q.push(connect[i]);
  62. d.push(tmpDist + 1);
  63. }
  64. }
  65. }
  66. return 0;//没有找到路径
  67. }
  68. public:
  69. int ladderLength(string beginWord, string endWord, unordered_set<string>& wordList) {
  70. int res= BFS(beginWord, endWord, wordList);
  71. return res;
  72. }
  73. };

参考

unordered_set::find时间复杂度
http://www.cplusplus.com/reference/unordered_set/unordered_set/find/

Complexity
Average case: constant.
Worst case: linear in container size.

发表评论

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

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

相关阅读

    相关 Word Ladder--LeetCode

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