LeetCode 126单词接龙II(官方解法)

2 篇文章 0 订阅
0 篇文章 0 订阅

给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:

每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。

说明:

如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

示例 1:

输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]

输出:
[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]

示例 2:

输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]

输出: []

解释: endWord “cog” 不在字典中,所以不存在符合要求的转换序列。

const int INF = 1 << 20;

class Solution {
private:
    unordered_map<string, int> wordId;
    vector<string> idWord;
    vector<vector<int>> edges;
public:
    vector<vector<string>> findLadders(string beginWord, 
                                       string endWord, vector<string>& wordList) {
        int id = 0;
        for (const string& word : wordList) {
            if (!wordId.count(word)) {
                wordId[word] = id++;
                idWord.push_back(word);
            }
        }//创建WORD-ID(unordered_map)映射以及ID-WORD映射(vector<string>)
        if (!wordId.count(endWord)) {
            return {};
        }//不存在目标单词,返回空
        if (!wordId.count(beginWord)) {
            wordId[beginWord] = id++;
            idWord.push_back(beginWord);
        }//添加起始单词
        edges.resize(idWord.size());//edges[i][j]表示ID为i与j的单词之间有边(只差一个字母))
        for (int i = 0; i < idWord.size(); i++) {
            for (int j = i + 1; j < idWord.size(); j++) {
                if (transformCheck(idWord[i], idWord[j])) {
                    edges[i].push_back(j);
                    edges[j].push_back(i);
                }
            }
        }//创建edges二维矩阵
        const int dest = wordId[endWord];//dest为目标单词的ID
        vector<vector<string>> res;//变化的单词路径
        queue<vector<int>> q;//BFS的队列
        vector<int> cost(id, INF);
        //cost[i] 表示 beginWord 对应的点到第 i 个点的代价(即转换次数)。
        //初始情况下其所有元素初始化为无穷大。
        //所有的单词耗费初始化为无限远
        q.push(vector<int>{wordId[beginWord]});//队列放入起始单词
        cost[wordId[beginWord]] = 0;
        while (!q.empty()) {
            //将起点加入队列开始广度优先搜索,队列的每一个节点中保存从起点开始的所有路径。
            vector<int> now = q.front();
            //取出与队列首单词相连的单词ID数组(相差一个字母的单词数组)为now
            q.pop();//弹出首单词ID
            int last = now.back();//last为now的最后一个ID,即相邻的最后一个单词
            /*
             对于每次取出的节点 now,每个节点都是一个数组,数组中的最后一个元素为当前路径的最后节点 last:
    		若该节点为终点,则将其路径转换为对应的单词存入答案;
    		若该节点不为终点,则遍历和它连通的节点(假设为 to )中
    		满足 cost[to]>=cost[now]+1的加入队列,
    		并更新 cost[to]=cost[now]+1。
    		如果 cost[to]<cost[now]+1,
    		说明这个节点已经被访问过,不需要再考虑。
            */
            if (last == dest) {//若到达终点单词,将其路径转换为对应的单词存入答案;
                vector<string> tmp;
                for (int index : now) {//对now数组中的每个单词ID进行遍历
                    tmp.push_back(idWord[index]);
                }
                res.push_back(tmp);
            } else {//不为终点,则遍历和它连通的节点(假设为 to )
                for (int i = 0; i < edges[last].size(); i++) {//对ID为last邻接的单词进行扫描
                    int to = edges[last][i];//to为邻接的单词ID
                    if (cost[last] + 1 <= cost[to]) {
                        cost[to] = cost[last] + 1;
                        vector<int> tmp(now);
                        tmp.push_back(to);
                        q.push(tmp);
                    }
                }
            }
        }
        return res;
    }

    bool transformCheck(const string& str1, const string& str2) {
        //检查它们是否可以通过改变一个字母进行互相转换。如果可以,则在这两个点之间建一条双向边。
        int differences = 0;//不同字母的个数
        for (int i = 0; i < str1.size() && differences < 2; i++) {
            if (str1[i] != str2[i]) {//对两个单词的字母一一对比,有不同的dif自增,大于1则跳出
                ++differences;
            }
        }
        return differences == 1;
    }
};

LeetCode官方题解链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值