首先,在有道面实习的时候,问到了这样的一个题目,就是说一个图像识别类似的问题,假设改图片中就是一个英语单词。该单词的长度是未知的,该单词的每一位都太模糊了,但是大概可以给几个候选。比如:原本的字母可能是a, 但是由于图像太模糊了,只能猜大概是 d,o,b 3个字母其中一个。那么最后要求输出所有的单词种类。
DFS的话。由于单词本身的长度太长的话,递归的深度可能太深了。当时我确实没想到什么好点子,今天突然做到word ladder 这道题,似乎是可以使用父节点之类的什么东西。没有想明白,如果大家有什么好的idea,可以告诉我,不胜感激。
PS:当时面试官提醒的是 DFS 加一个小trick之类的。至今还没想明白。
题目描述:
Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
- Only one letter can be changed at a time
- Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
要求最短的路径,BFS当然是首选。直接可以得到最短路径。因此代码如下:
const char alphabet[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
class Solution {
public:
int ladderLength(string start, string end, unordered_set
&dict) {
if(dict.size() == 0 || start == end)
{
return 0;
}
int curStep = 1;
int minStep = INT_MAX;
unordered_set
visited;
queue
prev;
queue
next;
prev.push(start);
int n = start.size();
string curWord;
string temp;
while(!prev.empty())
{
curStep += 1;
while(!prev.empty())
{
temp = prev.front();
//cout << curStep << " " << temp << endl;
prev.pop();
for(int i = 0 ; i < n; ++i)
{
curWord = temp;
for(int j = 0 ; j < 26; ++j )
{
if(curWord[i] == alphabet[j])
{
continue;
}
curWord[i] = alphabet[j];
//cout << curWord << endl;
if(curWord == end)
{
return curStep;
}
if(dict.find(curWord) != dict.end() && visited.find(curWord) == visited.end())
{
//cout << "xq" << endl;
visited.insert(curWord);
next.push(curWord);
}
curWord = temp; //!!!!!
}
}
}
swap(prev, next);
}
return 0;
}
};
对于上述代码,值得一提的是,每次在访问了当前单词之后,必须恢复原来的单词。
word ladder 2, 跟上面的题目一致,不再是输出长度,而是得到所有可能的路径,这里有个小窍门:
如果要输出所有的路径,如果对已经走过的路径进行保存的话,空间是会非常大的,因为每一个节点再最坏情况下有26个后继节点,由于这其实是一个树状的结构。我们可以通过保存每一个节点的父亲节点(唯一的),即可节省空间。
因此,代码如下:
class Solution {
private:
void buildPath( unordered_map
> & parent, const string start, vector
> &ret,
vector
&curPath, const string word)
{
// cout << "build path..." << endl;
curPath.push_back(word);
if(word == start)
{
ret.push_back(curPath);
reverse(ret.back().begin(), ret.back().end());
}
else
{
for(int i = 0 ; i < parent[word].size() ; ++i)
{
buildPath( parent, start,ret, curPath, parent[word][i]);
}
}
curPath.pop_back();
}
public:
vector< vector< string> > findLadders( string start, string end, unordered_set
& dict) { vector
> ret; //必须不包含重复的元素??? unordered_set
cur, next; cur.insert(start); unordered_set
visited; unordered_map
> parent; bool found = false; string curWord, tempWord; while(!cur.empty() && !found) { //cout << "xq" << endl; found = false; //将该层的所有节点入栈 for(auto it = cur.begin(); it != cur.end() ; ++it) visited.insert(*it); for(auto it = cur.begin() ; it != cur.end() ; ++it) { curWord = *it; for(int i = 0 ; i < curWord.size() ; ++i) { tempWord = curWord; for(char c = 'a' ; c <= 'z' ; ++c) { if(tempWord[i] == c) continue; tempWord[i] = c; if(tempWord == end) { found = true; //在这一层找到了该节点,下面的层就不用管了,但是当前层的单词得考虑完毕。所以不用break } if ( visited.count(tempWord) == 0 && ( dict.count(tempWord) > 0 || tempWord == end )) { parent[tempWord].push_back(curWord); next.insert(tempWord); } tempWord = curWord; } } } cur.clear(); swap(cur, next); } if(found) { vector
curPath; //为了下面的函数的递归调用 buildPath(parent, start, ret, curPath, end); } return ret; } };
。