跳转至

127.Word Ladder

Tags: Medium Breadth-first Search

Links: https://leetcode.com/problems/word-ladder/


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.

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.

Example 1:

Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output: 5

Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.

Example 2:

Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: 0

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        if (!wordSet.count(endWord)) return 0;

        unordered_map<string, int> path;
        path[beginWord] = 1;
        queue<string> q;
        q.push(beginWord);
        while (!q.empty()) {
            string word = q.front(); q.pop();
            for (size_t i = 0; i < word.size(); ++i) {
                string tmp = word;
                for (char ch = 'a'; ch <= 'z'; ++ch) {
                    tmp[i] = ch;
                    if (wordSet.count(tmp) && tmp == endWord) return path[word] + 1;
                    if (wordSet.count(tmp) && !path.count(tmp)) {
                        q.push(tmp);
                        path[tmp] = path[word] + 1;
                    }
                }
            }
        }

        return 0;
    }
};

最朴素的想法是将 'hit' 变为 'cog',那么我们发现这两个单词没有一个相同的字母,比如先将第一个 'h' 换成 'c',看看 'cit' 在不在字典中,发现不在,那么把第二个 'i' 换成 'o',看看 'hot' 在不在,发现在!然后尝试 'cot' 或者 'hog',发现都不在,那么就比较麻烦了,我们没法快速的达到目标单词,需要一些中间状态。

简单粗暴的方法就是brute force,遍历所有的情况,我们将起始单词的每一个字母都用26个字母来替换,比如起始单词 'hit' 就要替换为 'ait', 'bit', 'cit', .... 'yit', 'zit',将每个替换成的单词都在字典中查找一下,如果有的话,那么说明可能是潜在的路径,要保存下来。那么现在就有个问题,比如我们换到了 'hot' 的时候,此时发现在字典中存在,那么下一步我们是继续试接下来的 'hpt', 'hqt', 'hrt'... 还是直接从 'hot' 的首字母开始换 'aot', 'bot', 'cot' ...

这实际上就是BFS和DFS的区别,到底是广度优先,还是深度优先。讲到这里,不知道你有没有觉得这个跟什么很像?对了,跟迷宫遍历很像,迷宫中每个点有上下左右四个方向可以走,而这里有26个字母,就是二十六个方向可以走,本质上没有啥区别啊!

明确了要用BFS,我们可以开始解题了,为了提到字典的查找效率,我们使用HashSet保存所有的单词。然后我们需要一个HashMap,来建立某条路径结尾单词和该路径长度之间的映射,并把起始单词映射为1。既然是BFS,我们需要一个队列queue,把起始单词排入队列中,开始队列的循环,取出队首词,然后对其每个位置上的字符,用26个字母进行替换,如果此时和结尾单词相同了,就可以返回取出词在哈希表中的值加一。如果替换词在字典中存在但在哈希表中不存在,则将替换词排入队列中,并在哈希表中的值映射为之前取出词加一。如果循环完成则返回0。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        if (!wordSet.count(endWord)) return 0;

        queue<string> q;
        q.push(beginWord);
        int level = 0;
        while (!q.empty()) {
            for (size_t k = q.size(); k > 0; --k) {
                string word = q.front(); q.pop();
                if (word == endWord) return level + 1;
                for (size_t i = 0; i < word.size(); ++i) {
                    string tmp = word;
                    for (char ch = 'a'; ch <= 'z'; ++ch) {
                        tmp[i] = ch;
                        if (wordSet.count(tmp) && tmp != word) {
                            q.push(tmp);
                            wordSet.erase(tmp);
                        }
                    }
                }
            }
            ++level;
        }

        return 0;
    }
};

上述方法空间上的优化,其实并不需要一个unordered_map来记录路径,我们只需要去记录路径的层数,所以巧妙地增加一层循环,记录每一层地长度,这一层遍历完,层数+1。这里20行当找到一个存在于wordSet,就把这个tmp移除是因为,如果在后面又一次找到这个单词,那么路径一定比当前长,如果在之前找到,那么之前就已经删掉了,也不会现在找到,所以直接移除即可。

进一步思考:如果题目要求输出路径怎么办?

其实就是126.Word Ladder II