Word Ladder II -- leetcode

来源:互联网 发布:redis数据库有什么用 编辑:程序博客网 时间:2024/06/05 02:37

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) 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

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [    ["hit","hot","dot","dog","cog"],    ["hit","hot","lot","log","cog"]  ]

Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.

基本思路:

要找出变化的最短路径,则需要进行一次广度优先搜索,以求得路径最小长度。

再进行一次深度优先搜索,以记录这些能达到目的地的路径。


从一个节点,找到它的邻接点时,通过将每位字符吕a-z逐一进行变化,然后在字典中查找是否存在。

在第一次广度优先搜索之时,记录下每个节点所在树的层次,即距离起始点的最小长度。

然后在深度优先搜索时,利用上面得到的信息。可以避免死循环,以及比淘汰比更短路径更长的路径。

因为每次找邻接点时,检查它是比本节点更上一层的接点。


深度优先搜索,开始与广度优先执行相反的方向。 即如果广度优先执行从start开始, 则深度从end开始。 即自底向上到顶。

因为达到层次1时,0层肯定就是start,无需再计算。 反之,从顶到底的话,因为底部不一定都是end。

而最好的方式,当是广度优先搜索,从end开始。 而深度则从start开始,这样的好处,是省去了最后路径的一个reverse操作。


下面 代码是BFS从start开始,故在最后路径,需要一个reverse操作。

在代码在 leetcode上实际执行时间为476ms。

class Solution {public:    vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {        vector<vector<string>> ans;        vector<string> trace;        unordered_map<string, int> visited;        if (bfs(start, end, dict, visited))            dfs(start, end, visited, visited[end], trace, ans);                return ans;    }        bool bfs(string start, string end, const unordered_set<string> &dict, unordered_map<string, int> &visited) {        queue<string> q;        q.push(start);        int level = 0;        visited[start] = level;        bool found = false;        while (!found && !q.empty()) {            ++level;            int count = q.size();            while (count--) {                string word = q.front();                q.pop();                for (int i=0; i<word.size(); i++) {                    const char ch = word[i];                    for (char j='a'; j<='z'; j++) {                        word[i] = j;                        if (dict.find(word) != dict.end() && visited.find(word) == visited.end()) {                            q.push(word);                            visited[word] = level;                            if (word == end)                                found = true;                        }                    }                    word[i] = ch;                }            }        }        return found;    }        void dfs(const string &start, string &end, const unordered_map<string, int> &levelmap, int level, vector<string> &path, vector<vector<string>> &ans) {        auto iter = levelmap.find(end);        if (iter == levelmap.end() || iter->second != level) return;        if (level == 1) {            ans.push_back(path);            ans.back().push_back(end);            ans.back().push_back(start);            reverse(ans.back().begin(), ans.back().end());            return;        }                path.push_back(end);        for (int i=0; i<end.size(); i++) {            const char ch = end[i];            for (char j='a'; j<='z'; j++) {                end[i] = j;                dfs(start, end, levelmap, level-1, path, ans);            }            end[i] = ch;        }        path.pop_back();    }};



上面在BFS过程中,并未存储邻接点信息。即并未保持,树的父子关系。

在进行DFS过程中,邻接点仍需要进行重新计算。

偿试了一下,在BFS保存邻接点信息。每个结点保存了达到该结点的上一层的所有结点。

发现了BFS写起来要比上面复杂了。不再是那么单纯了: 那个visited,存入时间t和判断时间需要特殊考虑

虽然在visited上下了功夫,保证没有循环回路的产生;但无法避免同一层次结点之间的包含。

故在DFS搜索时,还是需要借助level递减这种方式,剔除,那些走了平行结点的路径。因为这些路径比最短路径要长。

此代码在leetcode上实际执行时间为1160ms。

大大的慢于上面的算法。 

而且空间利用比上面更多。

看来此算法不可取。


class Solution {public:    vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {        vector<vector<string>> ans;        unordered_map<string, unordered_set<string>> tree;        vector<string> path;        int level;        if (bfs(start, end, dict, tree, level))            dfs(start, end, tree, path, level, ans);                    return ans;    }        bool bfs(string start, string end, const unordered_set<string> &dict, unordered_map<string, unordered_set<string>> &tree, int &level) {        unordered_set<string> visited;        queue<string> q;        q.push(end);        bool found = false;        level = 0;        while (!found && !q.empty()) {            int count = q.size();            ++level;            while (count--) {                string word = q.front();                q.pop();                if (visited.find(word) != visited.end())                    continue;                visited.insert(word);                const string parent = word;                for (int i=0; i<word.size(); i++) {                    const char ch = word[i];                    for (char j='a'; j<='z'; j++) {                        word[i] = j;                        if (dict.find(word) != dict.end() && visited.find(word) == visited.end()) {                            q.push(word);                            tree[word].insert(parent);                            if (word == start)                                found = true;                        }                    }                    word[i] = ch;                }            }        }        return found;    }        void dfs(string start, string end, unordered_map<string, unordered_set<string>> &tree, vector<string> &path, int level, vector<vector<string>> &ans) {        path.push_back(start);        if (start == end) {            ans.push_back(path);        }        else if (level) {            const auto &parents = tree[start];            for (auto p: parents) {                dfs(p, end, tree, path, level-1, ans);            }        }        path.pop_back();    }};


0 0