赞
踩
题目:跳转至 127.单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 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; } };
(代码太长默认浏览器一复制过来就卡页面,换了谷歌能显示全就很优秀)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。