Word Ladder


Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

http://www.lintcode.com/en/problem/word-ladder/

Discussion


非常典型的BFS。用两个队列,current, next,分别表示当前层次和下一层,另设一个全局整数level,表 示层数(也即路径长度),当碰到目标状态,输出level 即可。
用unordered_set来实现判重。
在状态转移的时候,也就是改变一个字母以后,查该单词是否在词典中,而不要去对每个单词查词典里是否有距离为1的单词.

Solution


写这个题之前先写一遍level order traversal,熟悉BFS框架。

  1. 改成用一个queue
  2. 不需要额外的isVisted这个hash set,每次新的word记录到queue里的时候就把它从dict里面erase掉。
int ladderLength(string start, string end, unordered_set<string> &dict) {
        // write your code here
        int n = start.length();
        if(n==0 || n!=end.length() || dict.empty()) {
            return 0;
        }
        queue<string> currentNodes;
        int len = 2;
        currentNodes.emplace(start);
        dict.erase(start);
        while(currentNodes.empty() == false) {         
            int sz = currentNodes.size();//for each word in the current level
            for(int i=0; i<sz; i++) {
                string newStr = currentNodes.front();
                currentNodes.pop();
                for(int j=0; j<n; j++) {//for each letter in the current node
                    char tmp = newStr[j];
                    for(char c = 'a'; c<='z'; c++) {
                        if(c==tmp) continue;
                        newStr[j] = c;
                        if(newStr == end) {
                            return len;
                        }
                        if(dict.find(newStr) != dict.end()) {
                            currentNodes.push(newStr);
                            dict.erase(newStr);
                        }
                        newStr[j] = tmp;
                    }
                }
            }
            len++;
        }
        return 0;
    }

下面的写法思路更清晰,BFS,只是每个node的neighbors要自己去算而已。

Follow up

最爱下面这个写法。

// Time: O(n * d), n is length of the string, d is size of the dictionary
// Space: O(d)

class Solution {
public:
    int ladderLength(string start, string end, unordered_set<string> &dict) {
        int n = start.length();
        queue<string> level;
        int len = 1;
        level.emplace(start);
        while(level.empty() == false) {
            int current_size = level.size();
            for(int i=0; i<current_size; i++) {
                string cur = level.front();
                if(cur == end) return len;
                dict.erase(cur);
                level.pop();//remove cur from level
                bfs(cur, dict, level);//, update level with cur's neighbors
            }
            len++;
        }
        return 0;
    }

    void bfs(string s, unordered_set<string> &dict, queue<string> &level) {
        int n = s.length();
        for(int i=0; i<n; i++) {
            string tmp = s;
            for(char c = 'a'; c <= 'z'; c++) {
                tmp[i] = c;
                if(dict.find(tmp) != dict.end()) {
                    level.emplace(tmp);
                    dict.erase(tmp);//这里是为了make sure同一层不要有重复的word
                }
            }
        }
    }
};

用显式的两层level来写。方便扩展到下一题。

class Solution {
public:
    /**
      * @param start, a string
      * @param end, a string
      * @param dict, a set of string
      * @return an integer
      */
    int ladderLength(string start, string end, unordered_set<string> &dict) {
        int len = 0;
        if(start == end) return 2;
        unordered_set<string> level[2];//level[0]: current level, level[1]: next level
        level[len].emplace(start);
        unordered_set<string> visited;
        while(level[len%2].size() >0) {
            for(auto const& s : level[len%2]) {
                if(s == end) {
                    return len +1;
                }
                visited.emplace(s); // mark as visited before next level
                //get s's neighbors, i.e., one distance words in dict
                bfs(s, dict, visited, level[(len+1)%2]);
            }
            level[len%2].clear();
            len++;
        }
        return len + 1;
    }
    void bfs(string s, unordered_set<string> &dict, unordered_set<string> &visited,
    unordered_set<string> &nextlevel) {
        int n = s.length();
        for(int i=0; i<n; i++) {
            for(char c = 'a'; c<='z'; c++) {
                string tmp = s;
                tmp[i] = c;
                if(dict.find(tmp) != dict.end() && visited.find(tmp) == visited.end()) {
                    nextlevel.emplace(tmp);
                    //visited.emplace(tmp);//因为nextlevel用的unordered_set,同一层不可能有重复的了,如果是用vector的话这句就不能注释掉,因为同一层有重复的提交的时候会超时。
                }
            }
        }
    }
};

下面这个是双向的BFS,太高级了,leetcode上提交80ms,几乎最快。

//BFS, two-end method
//traverse the path simultaneously from start node and end node, and merge in the middle
//the speed will increase (logN/2)^2 times compared with one-end method
int ladderLength(string start, string end, unordered_set<string> &dict) {
    unordered_set<string> begSet, endSet, *set1, *set2;
    begSet.insert(start);
    endSet.insert(end);
    int h=1, K=start.size();
    while(!begSet.empty()&&!endSet.empty()){
        if(begSet.size()<=endSet.size()){   //Make the size of two sets close for optimization
            set1=&begSet;   //set1 is the forward set
            set2=&endSet;   //set2 provides the target node for set1 to search
        }
        else{
            set1=&endSet;
            set2=&begSet;
        }
        unordered_set<string> itmSet;   //intermediate Set
        h++;
        for(auto i=set1->begin();i!=set1->end();i++){
            string cur=*i;
            for(int k=0;k<K;k++){   //iterate the characters in string cur
                char temp=cur[k];
                for(int l=0;l<26;l++){  //try all 26 alphabets
                    cur[k]='a'+l;
                    auto f=set2->find(cur);
                    if(f!=set2->end())return h;
                    f=dict.find(cur);
                    if(f!=dict.end()){
                        itmSet.insert(cur);
                        dict.erase(f);
                    }
                }
                cur[k]=temp;
            }
        }
        swap(*set1, itmSet);
    }
    return 0;
}

results matching ""

    No results matching ""