126 单词接龙II

3 篇文章 0 订阅

题目

按字典 wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> … -> sk 这样的单词序列,并满足:

每对相邻的单词之间仅有单个字母不同。
转换过程中的每个单词 si(1 <= i <= k)必须是字典 wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
sk == endWord
给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord, s1, s2, …, sk] 的形式返回。

class Solution {
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        vector<vector<string>> result;//存储最终的所有最短路径
        queue<vector<string>> que;//队列辅助实现BFS
        unordered_set<string> s(wordList.begin(), wordList.end());//哈希表,如果已经访问过此节点则删除
        que.push({beginWord});//将初始节点入队列
        //层序遍历,访问每一个节点,即每一个可能的路径
        while(!que.empty()){
            unordered_set<string> visited;//存储该层访问过的单词
            //当前队列中存储的为该层的所有节点,遍历所有节点
            //每一个节点都是一条路径
            for(int i=que.size(); i>0; --i){
                auto cur=que.front();//取出当前的队首节点
                que.pop();
                auto tail=cur.back();//节点为路径,它的最后一个单词影响下一层的结果
                //如果该单词为endword,表示已经找到一条最短路径
                if(tail==endWord){
                    result.push_back(cur);
                    continue;//要在遍历完该层后才能返回所有结果
                }
                //从当前节点查找下一层节点
                //对当前的单词,遍历它的每一个字母,对每一个字母都尝试26个字母中的每一种情况,在哈希表中查找是否存在
                for(int j=0; j<tail.size(); ++j){
                    char tmp=tail[j];//记录初始的单词
                    for(char c='a'; c<='z'; ++c){
                        if(tmp==c) continue;//如果为初始单词跳过当前循环
                        tail[j]=c;
                        if(s.count(tail)){
                        	//找到一种情况,将新找到的单词加入到visited中,表示已经访问,给当前的路径添加新的单词,把新的路径入队,再将路径恢复成初始的状态,尝试下一种情况
                            visited.insert(tail);
                            cur.push_back(tail);
                            que.push(cur);
                            cur.pop_back();
                        }
                    }
                    tail[j]=tmp;//恢复初始的单词
                }
            }
            //如果当前层遍历结束后,result不是空,则表示已经找到了最短路径,返回它
            if(result.size()) return result;
            //否则,将该层已经访问过的单词从可能的结果中删掉,遍历下一层
            for(auto &word:visited) s.erase(word);
        }
        return {};//如果队列为空依然没有找到路径,则表示没有符合题意的转换路径
    }
};
  • 思路
    • 见代码
  • 为什么最短路径上前面已经出现过的元素不会在下一层出现
    • 两个不同层次但是相同字符串的结点后面的结构并不完全一样,因为它们前面走过的路径,经历过的结点不同, 剩余的可供选择的结点也不一样,因此后面的结构是不一样的。但是可以证明,下面层次的结点如果有到终点的路径,那它一定不是最短 的,故可以忽律掉它。证明如下:
      1. A与B是出现在树里不同节点的相同字符串,A在上层,B在下
      2. B字符串(黄色)经过若干步到达目标。这里有两种情况:
        2.1 这若干个字符串并没有包含A以前的元素,这样A后面必定存在与B后面一样的路径
        2.2 这若干个元素包含有A以前的元素,这样A以前的某个也存在于B后面的元素之后必 定存在与它在B的位置的后面一样的路径
      3. 综上所述,无论哪种情况,B后面都不会是最短路径,故可以忽律

参考题解一:解题思路的推导过程
参考题解二:解题思路

class Solution {
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        vector<vector<string>> result;
        queue<vector<string>> que;
        unordered_set<string> s(wordList.begin(),wordList.end());
        que.push({beginWord});
        while(!que.empty())
        {
            unordered_set<string> visited;
            for(int i=que.size();i>0;i--)
            {
                vector<string> cur=que.front();
                que.pop();
                string tail=cur.back();
                if(tail==endWord)
                {
                    result.push_back(cur);
                    continue;
                }
                for(int j=0;j<tail.size();j++)
                {
                    string tmp=tail;
                    for(char c='a';c<='z';c++)
                    {
                        tmp[j]=c;
                        //if(tmp==tail) continue;
                        if(s.count(tmp))
                        {
                            visited.insert(tmp);
                            cur.push_back(tmp);
                            que.push(cur);
                            cur.pop_back();
                        }

                    }
                }        
            }
            if(!result.empty()) return result;
            for(auto &word:visited) s.erase(word);        
        }
        return {};
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值