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:

  1. Only one letter can be changed at a time.
  2. 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的最短路径:

 
Paste_Image.png

PS:图中相互之间只相差一个字母的单词都是相邻节点。

 

利用BFS算法,维持两个集合: visited 和 wordSet

 
Paste_Image.png

从hit开始出发,找到唯一的相邻节点:hot, 把它放进visited中,第一次循环结束。 PS: 所谓找到相邻的节点,在题目中就是找出和该单词之相差一个字母的所有单词。请看最后代码的详细实现。

 
Paste_Image.png

查看hot节点的所有相邻节点(已经被访问过的hit除外),找到lot和dot, 放进visited中。第二次循环结束。

 
Paste_Image.png

找出新家进来的lot和dot的未被访问的相邻节点,分别是log和dog放进visited中。第三次循环结束。

 
Paste_Image.png

找出log的未被访问的相邻节点cog,放进结合中。第四次循环结束。由于cog就是endWord,任务结束,跳出循环。

 
Paste_Image.png

这里总共经历了四次循环,每循环一次,其实就是从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

posted @ 2018-02-26 21:14  乐乐章  阅读(224)  评论(0编辑  收藏  举报