LeetCode 127. Word Ladder
2016-03-28 16:07
417 查看
1. 题目描述
Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,
Given:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,”dot”,”dog”,”lot”,”log”]
As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
2. 解题思路
这道题目可以使用图的观点来看待他, 也就是找一个节点到另一个节点之间的最短路径长度问题, 但是图中, 求解这个问题一般是采用Dijkstra 方法求解的, 而且一般处理的还是无向带权图, 这里就不适用了。所以基本上就剩下两种思路DFS 和 BFS。我们一开始没有过多的考虑到底是使用 DFS 还是 BFS 的问题, 因为在一般情况下, DFS 和 BFS 都是可以处理这类问题的, 并且由于 DFS 写的比较顺手, 于是华丽丽的掉到坑里面去了。
对于这道题目, 非常适合使用BFS 的搜索框架, 搜索的层数就是 两者之间的距离
而像我们一开始试图采用的 DFS, 需要遍历整个搜索空间, 这个时间的消耗的巨大的。
3. code
这里需要说明的是, 我们这里采用的 BFS, 由于需要统计他的层数, 我们想到的是借助 层次遍历的思想, 利用两个队列的切换, 来实现层数的增加。在 GetNeighbours 这个函数中, 注释的那部分代码是一定需要添加的, 可以节省很大的搜索量, 如果没有这部分代码, 多个路径会访问相同的节点, 造成重复访问, 最直接的表现就是 TLE了。
class Solution { public: int ladderLength(string beginWord, string endWord, unordered_set<string>& wordList) { return Search(beginWord, endWord, wordList); } private: int Search(string startWord, string endWord, unordered_set<string> & wordList){ if (startWord == endWord) return 0; wordList.insert(endWord); vector<queue<string>> myqueue(2, queue<string>()); int count = 0; myqueue[0].push(startWord); int index = 0; while (!myqueue[0].empty() || !myqueue[1].empty()){ count++; while (!myqueue[index].empty()){ string front = myqueue[index].front(); myqueue[index].pop(); if (front == endWord) return count; unordered_set<string> toVisited; GetNeighbours(front, wordList, toVisited); for (string item : toVisited){ myqueue[1 - index].push(item); } } index = 1 - index; } return 0; } // 获取相邻节点 void GetNeighbours(string item, unordered_set<string> & wordList, unordered_set<string> & toVisited){ wordList.erase(item); for (int i = 0; i != item.size(); i++){ string tmp = item; for (int j = 0; j != 26; j++){ tmp[i] = 'a' + j; if (wordList.find(tmp) != wordList.end()){ toVisited.insert(tmp); wordList.erase(tmp); // new improvement 这句话一定要加, 可以节省很大的搜索量 } } } } };
4. 大神的代码
4.1 Demo1 BFS
他是使用toVisit 的 size 来标记这一层是否遍历完成的, ie, 在说明标记 TAG1 处,执行完这个for 循环之后, 上一层的节点是被全部访问过了的, 容器中剩余的都是这一层的带访问的节点。class Solution { public: int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) { wordDict.insert(endWord); queue<string> toVisit; addNextWords(beginWord, wordDict, toVisit); int dist = 2; while (!toVisit.empty()) { int num = toVisit.size(); // 我是说明标记 TAG1 for (int i = 0; i < num; i++) { string word = toVisit.front(); toVisit.pop(); if (word == endWord) return dist; addNextWords(word, wordDict, toVisit); } dist++; } } private: void addNextWords(string word, unordered_set<string>& wordDict, queue<string>& toVisit) { wordDict.erase(word); for (int p = 0; p < (int)word.length(); p++) { char letter = word[p]; for (int k = 0; k < 26; k++) { word[p] = 'a' + k; if (wordDict.find(word) != wordDict.end()) { toVisit.push(word); wordDict.erase(word); } } word[p] = letter; } } };
4.2 Bi-BFS 算法
由于 BFS 算法搜索到后期, 搜索的量是非常大的, 于是可以考虑使用 双向 BFS 搜索的方法, 每次从小端开始搜索, 这样相比 单纯的BFS 可以节省更多的搜索量class Solution { public: int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) { int srcLen = beginWord.size(), destLen = endWord.size(), dictSize = wordDict.size(); if((srcLen != destLen) || !srcLen || !dictSize) return 0; // abnormal cases, return 0; // abnormal cases, just return 0 unordered_set<string> unusedWords = wordDict; // words that never visited before unordered_set<string> activeWords[3]; int startSet = 0, endSet = 1, nextSet = 2, curDepth = 2, i; char tempC, j; activeWords[startSet].insert(beginWord); activeWords[endSet].insert(endWord); unusedWords.erase(beginWord); unusedWords.erase(endWord); while(!activeWords[startSet].empty()) { // do BFS on startSet for(auto it : activeWords[startSet]) { for(i = 0 ; i<srcLen; ++i) { for( tempC = it[i], j='a'; j<='z'; ++j) { if(tempC == j) continue; it[i] = j; if(activeWords[endSet].count(it)>0) return curDepth ;// if the new word is in the endSet, then we find a path if(unusedWords.count(it)) { // otherwise, if it is a new word that has never been visited activeWords[nextSet].insert(it); unusedWords.erase(it); } }// FOR j it[i] = tempC; } // FOR i } //FOR it ++curDepth; swap(startSet, nextSet); // swap the startSet and the nextSet if(activeWords[startSet].size() > activeWords[endSet].size()) swap(startSet, endSet); // if needed, switch the search direction activeWords[nextSet].clear(); } return 0; } };
相关文章推荐
- Cocos动作(1)
- JS 学习总结
- iOS数字显示保留几位小数点
- web开发推荐几款好用的必备工具
- ABP源码分析十四:Entity的设计
- LibSVM for Python 使用
- 用C++设计一个不能被继承的类
- ua curry shoes to elder sister
- Ubuntu 上搭建SVN 服务器
- 1000行代码写小游戏(二)
- 关于Mongodb的一些小问题
- 新增功能之一:Dashboard
- phpstorm配置
- lintcode:k Sum II
- 结对编程——阶段总结
- RESTFul中的那些事(1)---在RESTFul中,HTTP Put和Patch操作的差别?
- 怎样把pdf转换成excel表格格式
- JavaScript之基础-11 JavaScript 包装类对象(Math、Date、Number、Boolean)
- 1008. 数组元素循环右移问题 (20)
- 服务器共享时 删除链接,用新的用户名密码进行连接