127. Word Ladder(单词变换 bfs/双向 bfs)
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 transformed word must exist in the word list. Note that beginWord is not a transformed word.
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]
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.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same.
UPDATE (2017/1/20):
The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.
import queue class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: def word_diff(a,b): res = 0 for i in range(len(a)): if a[i] != b[i]: res+=1 if res >= 2: return res return res us = set(wordList) if endWord not in us: return 0 q1 = set([beginWord]) q2 = set([endWord]) used = set([beginWord]) res = 1 while len(q1)>0 and len(q2)>0: if len(q2) < len(q1): q1,q2 = q2,q1 temp = set() for top in q1: used.add(top) if top in q2: return res for word in wordList: if word_diff(top,word) == 1 and word not in used: temp.add(word) q1, q2 = q2, temp res+=1 return 0
class Solution { public: int ladderLength(string beginWord, string endWord, vector<string>& wordList) { unordered_set<string> us(wordList.begin(), wordList.end()); if (us.find(endWord) == us.end()) return 0; unordered_set<string> visited; unordered_set<string> q1; unordered_set<string> q2; visited.insert(beginWord); q1.insert(beginWord); q2.insert(endWord); int step = 0; while (!q1.empty() && !q2.empty()) { unordered_set<string> temp; if (q2.size() < q1.size()) { swap(q1,q2); } for(auto cur : q1) { //cout << cur << endl; if (q2.find(cur) != q2.end()) { return step+1; } visited.insert(cur); for (int j = 0; j < beginWord.size(); j++) { for (int k = 0 ; k < 26;k++) { string y = cur; y[j] = 'a' + k; if (y[j]==cur[j]) { continue; } if (us.find(y) != us.end() && visited.find(y) == visited.end()) { temp.insert(y); } } } } step ++; q1 = q2; q2 = temp; } return 0; } };
class Solution { public: int word_diff(string a, string b) { int res = 0; for(int i = 0; i < a.size();i++) { if (a[i]!=b[i]) res++; } return res; } int ladderLength(string beginWord, string endWord, vector<string>& wordList) { bool find = false; for(auto w: wordList) { if (w==endWord) { find = true; break; } } if (!find) return 0; unordered_set<string> q; unordered_set<string> q2; q.insert(beginWord); q2.insert(endWord); vector<bool> used(wordList.size(),false); int res = 0; while(!q.empty() &&!q2.empty()) { unordered_set<string> temp_q;// 下一层的结果 res++; if (q.size() > q2.size()) swap(q,q2); // 只遍历小的q, 这样可以减少遍历的子节点数量,减少耗时 for(auto cur : q) { // 遍历邻居 // if (q.find(cur) != q.end()) return res; // 不要在这判断,现在用的是unordered_set 保存每层的结果,没法像 queue 那样保存上一层结果。 // 在这q1 、q2永远无法相交。 for(int i = 0;i < wordList.size(); i++) { // 需要在这判断,新的子节点是否跟 q2 相交。 if (word_diff(wordList[i],cur)==1 && q2.find(wordList[i]) != q2.end()){ return res+1; } if (!used[i] && word_diff(wordList[i],cur)==1) { used[i] = true; temp_q.insert(wordList[i]); } } } q = q2; q2 = temp_q; } return 0; } };
class Solution { public: int word_diff(string a, string b) { int res = 0; for(int i = 0; i < a.size();i++) { if (a[i]!=b[i]) res++; } return res; } int ladderLength(string beginWord, string endWord, vector<string>& wordList) { queue<string> q; q.push(beginWord); vector<bool> used(wordList.size(),false); int res = 0; while(!q.empty()) { int sz = q.size(); res++; cout << res << endl; for(int i = 0; i < sz;i++) { string cur = q.front();q.pop(); if (cur == endWord) return res; // 遍历邻居 for(int i = 0;i < wordList.size(); i++) { if (!used[i] && word_diff(wordList[i],cur)==1) { used[i] = true; q.push(wordList[i]); } } } } return 0; } };
这道题是经典的广度有优先搜索的例子,也是Dijkstra's algorithm的变形。
以题目给出的例子为例,其实就是在所有路径的权重都为1的情况下求出下列无向图中从节点hit
到节点cog
的最短路径:
PS:图中相互之间只相差一个字母的单词都是相邻节点。
利用BFS算法,维持两个集合: visited 和 wordSet
从hit开始出发,找到唯一的相邻节点:hot, 把它放进visited中,第一次循环结束。 PS: 所谓找到相邻的节点,在题目中就是找出和该单词之相差一个字母的所有单词。请看最后代码的详细实现。
查看hot节点的所有相邻节点(已经被访问过的hit除外),找到lot和dot, 放进visited中。第二次循环结束。
找出新家进来的lot和dot的未被访问的相邻节点,分别是log和dog放进visited中。第三次循环结束。
找出log的未被访问的相邻节点cog,放进结合中。第四次循环结束。由于cog就是endWord,任务结束,跳出循环。
这里总共经历了四次循环,每循环一次,其实就是从beginWord想endWord变化的一步,因此循环的次数(加上1)就是从beginWord想endWord转变经历的 number of steps。
1 class Solution: 2 def ladderLength(self, beginWord, endWord, wordList): 3 """ 4 :type beginWord: str 5 :type endWord: str 6 :type wordList: List[str] 7 :rtype: int 8 """ 9 wordList = set(wordList) 10 visited = [beginWord] 11 visited = set(visited) 12 dist = 1 13 14 while endWord not in visited: 15 temp = set() 16 for word in visited: 17 for i in range(len(word)): 18 newwordL = list(word) 19 for ch in 'qwertyuiopasdfghjklzxcvbnm': 20 newwordL[i] = ch 21 newWord = ''.join(newwordL) 22 if newWord in wordList: 23 temp.add(newWord) 24 wordList.remove(newWord) 25 26 dist += 1 27 if len(temp) == 0: # if 0, it never gets to the endWord 28 return 0 29 30 visited = temp 31 32 return dist
参考链接:https://www.jianshu.com/p/753bd585d57e