您的位置:首页 > 职场人生

有道的一道面试题 突然觉得跟word ladder有些关系

2014-05-26 13:45 176 查看
首先,在有道面实习的时候,问到了这样的一个题目,就是说一个图像识别类似的问题,假设改图片中就是一个英语单词。该单词的长度是未知的,该单词的每一位都太模糊了,但是大概可以给几个候选。比如:原本的字母可能是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;
}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: