leetcode刷题-126. 单词接龙 II

126. 单词接龙 II

给定两个单词(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" 不在字典中,所以不存在符合要求的转换序列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-ladder-ii

思路

判断两个单词是否能够转换可以抽象成图,能转换即在两个单词之间构建一条无向边。需要一个check()函数
需要完成两个单词的互相查找,每个单词存在Id,使用哈希表完成单词到Id的查找。使用vector完成Id到单词的查找。数据结构分别为ordered_map<string,int>,vector<string>
需要edges来表示单词的连接关系,因为一个单词可与多个单词转换,使用vector<vector<int>>表示。
因为要求最短路径,使用int cost[]数组,表示路径长度,如何判断下一个节点是否比当前节点路径短,也就是说下一个节点是否经历过,因为一个节点可以存在多个上节点,也会存在多个下节点。cost[now] + 1<=cost[to]即表示下一节点还未经历,如果大于,则说明经历过了。

代码

const int INF = 1 << 20;

class Solution {
private:
    unordered_map<string,int> wordId;//word到Id的映射
    vector<string> idWord;//Id到word的映射
    vector<vector<int>> edges;//边
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        int id = 0;
        //建立映射
        for (const auto& word:wordList){
            if (!wordId.count(word)){
                wordId[word] = id++;
                idWord.push_back(word);
            }
        }

        //如果wordList中不存在endWord,返回{};
        if (!wordId.count(endWord))
            return {};
        
        //如果wordList不存在beginWord,wordId中需要添加
        if (!wordId.count(beginWord)){
            wordId[beginWord] = id++;
            idWord.push_back(beginWord);
        }

        //初始化边,edges的类型为vector<vector<int>>,有多少节点就有多少个vector<int>,然后存储与这个节点相接的下一个节点。
        edges.resize(idWord.size());

        //构建边;双循环,两两检查
        for (int i = 0;i < idWord.size();++i){
            for (int j = i + 1;j <idWord.size();++j){
                //两两检查
                if (check(idWord[i],idWord[j])){
                    //边是双向的,一个节点可以有好几个下节点
                    edges[i].push_back(j);
                    edges[j].push_back(i);
                }
            }
        }

        const int dest = wordId[endWord];//终点的标志数值
        vector<vector<string>> res;//结果
        queue<vector<int>> q;//深度优先搜索,队列,存储路径数组
        vector<int> cost(id,INF);//每条路径的长度,初始化为无限长
        q.push(vector<int>{wordId[beginWord]});//初始化队列,从beginWord开始
        cost[wordId[beginWord]] = 0;//长度从0开始
        //队列空,表示遍历了所有节点
        //如何判断是否是最小路径,如果有一条路径到达终点,和此条路径同时放入队列的必然是同一长度,且不会有新的路径放入队列,这就实现了最小路径。
        while (!q.empty()){
            //当前路径数据
            vector<int> now = q.front();
            q.pop();
            //当前路径最后一个节点
            int last = now.back();
            //若等于endWord的Id,输出当前路径
            if (last == dest) {
                vector<string> tmp;
                for (int index : now) {
                    tmp.push_back(idWord[index]);
                }
                res.push_back(tmp);
            }else {//若不等于,往下遍历节点,根据存储在edges中的顺序
                for (int i = 0; i < edges[last].size(); i++){
                    int to = edges[last][i];
                    //如果下一个节点经历过,长度必不可能大于当前节点长度+1,没有经历过,长度加1。将下一节点放入当前路径数组,将当前路径数组放入队列。
                    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 check(string& a,string& b){
        int diff = 0;
        for (int i = 0;i < a.size()&&diff < 2;++i){
            if (a[i]!=b[i])
                diff++;
        }
        return diff == 1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值