给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

  1. 输入:
  2. beginWord = "hit",
  3. endWord = "cog",
  4. wordList = ["hot","dot","dog","lot","log","cog"]
  5. 输出: 5
  6. 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
  7. 返回它的长度 5

BFS

image.png

  • 如果一开始就构建图,每一个单词都需要和除它以外的另外的单词进行比较,复杂度是 O(N \rm{wordLen})O(NwordLen),这里 NN 是单词列表的长度;
  • 为此,我们在遍历一开始,把所有的单词列表放进一个哈希表中,然后在遍历的时候构建图,每一次得到在单词列表里可以转换的单词,复杂度是O(26×wordLen),借助哈希表,找到邻居与 NN 无关;
  • 使用 BFS 进行遍历,需要的辅助数据结构是:队列;visited 集合。说明:可以直接在 wordSet (由 wordList 放进集合中得到)里做删除。但更好的做法是新开一个哈希表,遍历过的字符串放进哈希表里。这种做法具有普遍意义。

链接

  1. class Solution {
  2. public:
  3. int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
  4. unordered_set<string> wordListSet(wordList.begin(), wordList.end());
  5. unordered_set<string> visited;
  6. queue<string> queueTodo;
  7. if (wordListSet.find(endWord) == wordListSet.end()) return 0; //注意先查找结果是否存在单词列表种
  8. int count = 1;
  9. queueTodo.push(beginWord);
  10. while (!queueTodo.empty()) {
  11. int len = queueTodo.size();
  12. for (int i = 0; i < len; i++) {
  13. string curWord = queueTodo.front(); queueTodo.pop();
  14. for (int j = 0; j < curWord.size(); j++) {
  15. char c = curWord[j];
  16. for (int k = 0; k < 26; k++) {
  17. curWord[j] = 'a' + k;
  18. if (curWord[j] == c) continue;
  19. if (curWord == endWord) return count+1; //不能用count++; 会加不上
  20. if (wordListSet.find(curWord) != wordListSet.end() && visited.find(curWord) == visited.end()) {
  21. queueTodo.push(curWord);
  22. visited.insert(curWord);
  23. }
  24. }
  25. curWord[j] = c;
  26. }
  27. }
  28. count++;
  29. }
  30. return 0;
  31. }
  32. };

双向BFS

从两端开始找,知道两端发生重合时,则说明找到了。

  1. class Solution {
  2. public:
  3. int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
  4. unordered_set<string> wordSet(wordList.begin(), wordList.end());
  5. //双向BFS 每次选择较小的一个,是因为较小的扩散的范围小,可以减少搜索。
  6. if (wordSet.find(endWord) == wordSet.end()) return 0;
  7. //用set 方便查找是否在另一个set里面,如果用队列的话,查找的时间复杂度为O(n)
  8. unordered_set<string> beginSet, endSet, visited, tmp;
  9. int resCount = 1;
  10. beginSet.insert(beginWord);
  11. endSet.insert(endWord);
  12. //bfs start here
  13. while(!beginSet.empty() && !endSet.empty()) {
  14. if (beginSet.size() > endSet.size()) {
  15. tmp = beginSet;
  16. beginSet = endSet;
  17. endSet = tmp;
  18. }
  19. tmp.clear();
  20. for (string curWord : beginSet) {
  21. for (int j = 0; j < curWord.size(); j++) {//依次换每个字符,与表中的单词做匹配
  22. char c = curWord[j];
  23. for (int k = 0; k < 26; k++) {
  24. curWord[j] = 'a' + k;
  25. if (endSet.find(curWord) != endSet.end()) {
  26. return resCount + 1;
  27. }
  28. if (wordSet.find(curWord) != wordSet.end() && visited.find(curWord) == visited.end()) {
  29. visited.insert(curWord);
  30. tmp.insert(curWord);
  31. }
  32. }
  33. curWord[j] = c;
  34. }
  35. }
  36. beginSet = tmp;// 当前层遍历完,清空,继续下一层遍历。 因为没有queue的出队,所以直接和一个set交换;
  37. resCount++;
  38. }
  39. return 0;
  40. }
  41. };