LeetCode:126 单词接龙 II BFS+DFS+剪枝

给定两个单词(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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

不同于【LeetCode:127 单词接龙】,这题不是求最短长度,而是求所有的最短序列,这就比较蛋疼棘手 了

因为要求所有的最短的序列,那么肯定要bfs,而且bfs的上界就是当前长度 = 最短序列的长度(上面那个题求的,已知)

那么直接dfs呢?会超时,这里有一个非常巧妙的剪枝:

在bfs的时候,我们 不再使用双端bfs ,而是用从起点出发的bfs,对于每次bfs到的节点,我们都记录它第一次出现的深度,方便我们等下dfs的时候用

如果dfs的时候,遇到一个点,他的深度不是我们bfs记录过的深度,说明这条路没有按照我们bfs时的策略来走,肯定不是最短,这个时候直接放弃搜索

如图:2节点在bfs时第一次出现是在第二层,如果我们在第二层以外的层碰到2节点,说明走的不是最短路,直接放弃这个分支
在这里插入图片描述

这个优化使得代码直接可以AC,非常重要的剪枝

代码

class Solution {
public:
    int len;
    vector<vector<string>> ans;
    unordered_map<string, int> map; // 哈希map存层数
    // bfs求最短距离顺便标记节点第一次出现的层数
    int ladderLength(string beginWord, string endWord, vector<string>& wordList)
    {
        unordered_set<string> reachable(wordList.begin(), wordList.end());
        if(reachable.find(endWord) == reachable.end()) return 0;
        unordered_set beginSet {beginWord};
        int ans = 1;
        while(!beginSet.empty())
        {
            unordered_set<string>::iterator i;
            for(i=beginSet.begin(); i!=beginSet.end(); i++) reachable.erase(*i);  
            ans += 1;     
            unordered_set<string> this_step;
            for(i=beginSet.begin(); i!=beginSet.end(); i++)
            {
                for(int j=0; j<(*i).length(); j++)
                {
                    string s = *i;
                    for(char c='a'; c<='z'; c++)
                    {
                        s[j] = c;
                        if(reachable.find(s) != reachable.end())
                        {
                            this_step.insert(s);
                            map[s] = ans;
                            if(s==endWord) return ans;  
                        }
                    }
                }
            }
            beginSet = this_step;  
        }
        return 0;
    }
    // dfs+剪枝 unv = un visited = 没有被访问过的点的集合
    void dfs(int l, string& endWord, vector<string> &Ans, unordered_set<string>& unv)
    {
        if(l>len) return;
        if(l==len && Ans.back()==endWord) {ans.push_back(Ans); return;}
        string bk = Ans.back();
        for(int pos=0; pos<bk.length(); pos++)
        {
            for(char c='a'; c<='z'; c++)
            {
                string s=bk; s[pos]=c;
                if(unv.find(s)!=unv.end())
                {
                    // 重要剪枝:如果不是在已知的层出现,说明不是最短,跳过
                    if(map[s]!=l+1) continue;
                    unv.erase(s);
                    Ans.push_back(s);
                    dfs(l+1, endWord, Ans, unv);
                    Ans.pop_back();
                    unv.insert(s);
                }
            }
        }
    }
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList)
    {
        len = ladderLength(beginWord, endWord, wordList);
        if(len==0) return ans;
        vector<string> Ans{beginWord};
        unordered_set<string> unv(wordList.begin(), wordList.end());
        dfs(1, endWord, Ans, unv);
        return ans;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值