LeetCode每日一题--127.单词接龙(广度优先搜索)

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

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

说明:

  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设
    beginWord 和 endWord 是非空的,且二者不相同。
class Solution {
public:   	
	int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
            
        }
};

思路:
广度优先搜素,先把beginWord加入队列,队列不为空时,取队首单词在wordList中寻找转换有效且未被访问过的单词加入队列,循环直至找到endWord。

class Solution {
public:
    bool IsValidTrans(string a,string b){  //是否只改动一个字符
        int count=0;
        for(int i=0;i<a.length();++i){
            if(a[i]!=b[i])
                ++count;
        }
        if(count==1)
            return true;
        return false;
    }
    bool IsnotVisited(vector<string>& VisitedList,string s){  //是否访问过
        for(auto x:VisitedList){
            if(x==s)
                return false;
        }
        VisitedList.push_back(s);  //没有访问过就加入
        return true;
    }
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        int len=wordList.size();
        queue<string> que;
        que.push(beginWord);
        int sum=0;
        vector<string> VisitedList;
        while(!que.empty()){
            string tmp=que.front();
            que.pop();
            for(auto x:wordList){
                if(IsValidTrans(x,tmp) && IsnotVisited(VisitedList,x)){//转换有效且没有被访问过
                    que.push(x);
                    if(x==endWord)
                        return sum+1;
                }
            }
            ++sum;
        }
        return 0;
    }
};

上述给出的最短距离是不准的,因为最短距离是直接计算了入列后的单词挨个出列的个数,举例:
hit->hot hig->cot got hog->…
得出的结果就是1+2+3+…
修改增加一层循环,把最短距离计数放在循环外,使得每次改动一次之后的单词不会额外触发计数的增加,上述结果也成为1+1+1+…

class Solution {
public:
    bool IsValidTrans(string a,string b){  //是否只改动一个字符
        int count=0;
        for(int i=0;i<a.length();++i){
            if(a[i]!=b[i])
                ++count;
        }
        if(count==1)
            return true;
        return false;
    }
    bool IsnotVisited(vector<string>& VisitedList,string s){  //是否访问过
        for(auto x:VisitedList){
            if(x==s)
                return false;
        }
        VisitedList.push_back(s);  //没有访问过就加入
        return true;
    }
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        int len=wordList.size();
        queue<string> que;
        que.push(beginWord);
        int sum=1;
        vector<string> VisitedList;
        while(!que.empty()){
            int i=0;
            int len=que.size();
            while(i++<len){  //方便计数,把每一个单词单个变动一次的所有结果都循环完成
                string tmp=que.front();
                que.pop();
                for(auto x:wordList){
                    if(IsValidTrans(x,tmp) && IsnotVisited(VisitedList,x)){//转换有效且没有被访问过
                        que.push(x);
                        if(x==endWord)
                            return sum+1;
                    }
                }
            }
            ++sum;
        }
        return 0;
    }
};

结果正确但运行超时。开始抄答案(咳,学习),下面放官方题解
双向广度优先搜索:
借用从beginWord到endWord,和endWord到beginWord两个同时进行的广度搜索每次各扩展一层节点,当beginWord存在一个节点在存储从endWord扩展的距离数组中有值(或者相反)时,就找到了最短路径,停止搜索。

class Solution {
public:
    unordered_map<string,int> wordId;  //哈希表存储单词和单词对应序号
    vector<vector<int>> edge;  //二维数据作邻接列表
    int nodeNum=0;  //哈希表存放的单词的首位序号由0开始

    void addWord(string& word){
        if(!wordId.count(word)){  //计算word是否在wordId中
            wordId[word]=nodeNum++;  //如果在,当前nodeNum给他并nodeNum加1,即给每个单词标号
            edge.emplace_back();  //同时增加一条边
        }
    }

    void addEdge(string& word){
        addWord(word);
        int id1=wordId[word];  //获取word对应的标号
        for(char& it:word){
            char tmp=it;  //对单词的每一个字符换成 * 成为虚拟节点,加入哈希表wordID并得到一个对应序号
            it='*';
            addWord(word);
            int id2=wordId[word];  //获取变动一个字符的单词的序号
            edge[id1].push_back(id2);  //二维数组(邻接矩阵)存储边的信息,即x两个单词间的转换有效
            edge[id2].push_back(id1);
            it=tmp;   //还原单词
        }
    }

    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        for(string& word:wordList)  //建图,对每一个单词枚举可能的虚拟节点并通过edge连接对应的序号
            addEdge(word);
        addEdge(beginWord);  //并放入beginWord
        if(!wordId.count(endWord))  //如果endWord不在给出的字典内,不可能接龙成功
            return 0;

        vector<int> disBegin(nodeNum,INT_MAX);  //创建nodeNum大小的数组并初始化最大值,统计转换到每个标号的单词所需要的步数
        int beginId=wordId[beginWord];  //获取beginWord对应的标号
        disBegin[beginId]=0;  //距离从0开始计数
        queue<int> queBegin;  //创建从beginWord开始扩展的队列,存入对应标号
        queBegin.push(beginId);

        vector<int> disEnd(nodeNum,INT_MAX);  //创建nodeNum大小的数组并初始化最大值,统计转换到每个标号的单词所需要的步数
        int endId=wordId[endWord];  //获取endWord对应的标号
        disEnd[endId]=0;   //距离从0开始计数
        queue<int> queEnd;  //创建从beginWord开始扩展的队列,存入对应标号
        queEnd.push(endId);

        while(!queBegin.empty() && !queEnd.empty()){  //当两个队列均不为空时
            int queBeginSize=queBegin.size();
            for(int i=0;i<queBeginSize;++i){
                int nodeBegin=queBegin.front();  //从beginId往下走,取队首存放的标号
                queBegin.pop();
                if(disEnd[nodeBegin]!=INT_MAX)  //在从底往上记录距离的数组中找到从上往下查找的单词,即搜索到终点,因为加了虚拟节点,所以对半除加一
                    return (disBegin[nodeBegin]+disEnd[nodeBegin])/2+1;
                for(int& it:edge[nodeBegin]){  //遍历标号对应的边集,获取能转换成功的单词标号
                    if(disBegin[it]==INT_MAX){  //把能转换成功的单词距离记录到disBegin中,每层(单次能成功变换对应的单词的最短距离数目)加1
                        disBegin[it]=disBegin[nodeBegin]+1;
                        queBegin.push(it);  //加入队列,用于循环查找下一层能成功转换的单词
                    }
                }
            }

            int queEndSize=queEnd.size();
            for(int i=0;i<queEndSize;++i){
                int nodeEnd=queEnd.front();  //从endId往上走,取队首存放的标号
                queEnd.pop();
                if(disBegin[nodeEnd]!=INT_MAX)  //同理,搜索到终点
                    return (disBegin[nodeEnd]+disEnd[nodeEnd])/2+1;
                for(int& it:edge[nodeEnd]){  //赋值每层成功转换的最短距离
                    if(disEnd[it]==INT_MAX){
                        disEnd[it]=disEnd[nodeEnd]+1;
                        queEnd.push(it);
                    }
                }
            }
        }
        return 0;
    }
};

(代码太长默认浏览器一复制过来就卡页面,换了谷歌能显示全就很优秀)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值