leetcode.127:单词接龙

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

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

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

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

输出: 5

解释: 一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
返回它的长度 5。
示例 2:

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

输出: 0

解释: endWord “cog” 不在字典中,所以无法进行转换。
通过次数46,730提交次数109,825

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-ladder
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:
这是一道图的广度优先搜索,将字符能够相互转化视为图中两个节点所相连的边,此时通过以beginWord出发进行广度优先搜索遍历,期间记录遍历到每个位置的步数,可以获得最短的长度。难点有三个,一是将字符集抽象为图并选择合适的数据结构进行图的构建;二是此题目可以通过双向bfs来优化宽度优先搜索的时间复杂度;三是广度优先遍历过程中的已访问的设置,和搜索结束的条件设置。下面分别说明三个难点的解决方式:
1. 对于图的构建,可以使用map,key值中存储字符串,而value中存储为与该字符串相连即能够以题目条件相互转化的字符串的vector,亦即是以邻接表的方式构建图。因此需要实现一个判断是否相连的函数,之后双层遍历wordlist中的所有字符串,构建所有字符串的邻接表即可。需要注意的是,需要将beginword加入wordlist。
2. 双向bfs应用的场景是知道遍历要找的路径的起点和终点,分别从两边向中间进行遍历,当两边访问到相同的节点的时候,即说明找到了一条最短的路径,记录此时的长度即可。相对于单向的bfs,双向bfs优化了搜索空间,降低了时间复杂度。在实现的时候,需要设置两个遍历所需的队列,同时,为了减少遍历结束的条件判断的复杂度,需要一定的规则。本题中,默认先对一个队列进行中的队头元素进行bfs,将它的所有相邻接点入队并修改访问记录数组,同时,在每个元素入队的时候在另一个访问数组中查找这个元素,如果找到则说明满足退出条件,记录此时的结果,没有找到的话修改访问记录数组,注意:访问记录数组中需要同时记录到这个节点的步数。之后对另一个队头元素做相同的操作直至遍历结束或者找到结果。
3. 在本题中,无论是双向bfs和单向bfs都需要特别的设置访问数组,因为需要记录遍历到当前节点所走的步数,以便返回最后的结果。而对于本题的双向bfs,需要频繁的进行元素的查找。因此使用
stl::set(pair<string,int>)作为访问数组,而搜索结束的条件在2中已经详细说明。

ps:我的解法里,双向bfs仅仅快了400ms,大概20%,大概是太垃圾了吧,同时,写代码的时候要注意细节,在判断队列是否为空的时候切记要检查叹号是不是该用,特别是要同时判断两个队列的时候,少打一个叹号找了半小时。淦!

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        queue<pair<string,int>> q1;
        queue<pair<string,int>> q2;
        unordered_map<string,int> fv;
        unordered_map<string,int> bv;
        unordered_map<string,vector<string>> graph;

        constructGraph(wordList,beginWord,graph);

        q1.push(pair<string,int>(beginWord,1));
        q2.push(pair<string,int>(endWord,1));
        fv[beginWord] = 1;
        bv[endWord] = 1;

        while(!q1.empty()&&!q2.empty())
        {

            int ret = visitWordNode(q1,fv,bv,graph);
            if(ret>-1)return ret;
            ret = visitWordNode(q2,bv,fv,graph);
            if(ret>-1)return ret;
            //cout<<ret<<endl;
        }
        
        return 0;
    }

    int visitWordNode(queue<pair<string,int>>& Q,unordered_map<string,int>& visit,unordered_map<string,int>& anovisit,
    unordered_map<string,vector<string>>& graph)
    {
        string tempStr = Q.front().first;
        int step = Q.front().second;
        Q.pop();
        for(auto str : graph[tempStr])
        {
            if(anovisit.find(str)!=anovisit.end())
            {
                return step+anovisit[str];
            }
            if(visit.find(str)==visit.end())
            {
                Q.push(pair<string,int>(str,step+1));
                visit[str] = step+1;
            }
        }
        return -1;
    }

    bool isConnect(string &a,string &b)
    {
        int len = a.size();
        int count = 0;
        for(int i = 0;i<len;i++)
        {
            if(a[i]!=b[i])
            {
                count++;
            }
        }
        if(count==1)return true;
        return false;
    }

    void constructGraph(vector<string>& wordstring,string &beginWord,
    unordered_map<string,vector<string>> &graph)
    {
        wordstring.push_back(beginWord);
        for(int i = 0;i<wordstring.size();i++)
        {
            graph[wordstring[i]] = vector<string>();
        }

        for(int i = 0;i<wordstring.size();i++)
        {
            for(int j = i+1;j<wordstring.size();j++)
            {
                if(isConnect(wordstring[i],wordstring[j]))
                {
                    graph[wordstring[i]].push_back(wordstring[j]);
                    graph[wordstring[j]].push_back(wordstring[i]);
                }
            }
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值