Leetoce 127 Word Ladder
题目原文
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 transformed word must exist in the word list. Note that beginWord is not a transformed word.
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]
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.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same
题意分析
题目要求求beginword到endword的一个最短转化次数,其中中间word必须在wordList中,且endWord也在其中。每次转化只能改变一个字符,如果没有相应方法,返回0。
解法分析
此题类似于单源最短路径问题,通常采用广度优先搜索策略,由于是寻找一个最优解,所以是分支界限法的应用。分支界限法用于求解解树中的最优解,它采用广度优先搜索策略,每一个活节点只有一次机会成为可扩展节点,当一个节点成为可扩展节点时,遍历其所有儿子节点,并将活节点放入队列中,当同层可扩展节点遍历完并出队列后,开始下一层可扩展节点的遍历。如果解树带权,往往需要用优先队列来存储可扩展节点,pop出优先级最高的节点。类似于回溯法,分支界限法可以通过设定上界函数和剪枝函数来减少搜索次数。
本题中startWord和endWord分别作为起点和终点,与startword相差一个字符的字典元素作为与起点相邻的活节点点,加入可扩展队列中,每得到一个新的可扩展节点,字典中就需要删除相应元素,因为不可能有别的可扩展节点以该点为活节点了(贪心选择路径最短)。由于采用的广度优先搜索,所以在某一层达到返回条件,此时的层数(变换次数)一定是最小的。为了记录搜索层数,需要记录每一层活节点的个数,将该层活节点pop完后res++。如果可扩展节点队列没有元素了且程序没有返回,代表不存在相应变换。C++代码如下:
class Solution {
private:
int n;
public:
bool oneCh(string a,string b){
int i;
int count=0;
for(i=0;i<n;i++){
if(a[i]==b[i])
count++;
}
return (count==n-1)?1:0;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
int res=2;
deque<string> myQue;
int sig=0;
for(string kk:wordList){
if(kk==endWord){
sig=1;
break;
}
}
if(sig==0)
return 0;
n=beginWord.size();
int i,j;
string temp;
myQue.push_back(beginWord);
int number;
while(!myQue.empty()){//one cycle operate on one layer of the tree
number=myQue.size();
for(i=0;i<number;i++){
temp=myQue[0];
myQue.pop_front();
for(j=0;j<wordList.size();j++){
if(oneCh(temp,wordList[j])){
if(wordList[j]==endWord)
return res;
myQue.push_back(wordList[j]);
wordList.erase(wordList.begin()+j);
j--;
}
}
}
res++;
}
return 0;
}
};
上述方法可以用2-end BFS来做,也就是从start和end同时进行广度优先搜索,提高了效率,C++代码如下:
class Solution {
public:
int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) {
unordered_set<string> head, tail, *phead, *ptail;
head.insert(beginWord);
tail.insert(endWord);
int dist = 2;
while (!head.empty() && !tail.empty()) {
if (head.size() < tail.size()) {//如此交替,可以保证从start和end的进度相当
phead = &head;
ptail = &tail;
}
else {
phead = &tail;
ptail = &head;
}
unordered_set<string> temp;
for (auto itr = phead -> begin(); itr != phead -> end(); itr++) {
string word = *itr;
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 (ptail -> find(word) != ptail -> end())
return dist;
if (wordDict.find(word) != wordDict.end()) {
temp.insert(word);
wordDict.erase(word);
}
}
word[p] = letter;
}
}
dist++;
swap(*phead, temp);
}
return 0;
}
};