原题:
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
.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
刚看这题可能没有思路,但是把变换过程在纸上画画之后就明白怎么做了。以下是题目的抽象图。
即从图上找寻从起始节点到目标节点的最短路径问题。这是图的遍历问题,使用的方法无非BFS和DFS,要找最短路径,DFS只会一路到底,无疑BFS最合适。用BFS遍历图,一旦碰到目标节点即退出,此时路径即是最短路径。
注意:
(1)BFS搜索应该用队列来模拟;
(2)应该用一个数组类型的数据结构来记录已被遍历的节点,为求速度,可以使用unordered_set或set(竟然会超时);
(3)遍历时,应该将与某个节点相邻的未访问节点push进队列中,但怎么知道两个节点相邻?一个个搜索比较会超时,网上有一种很巧妙的方法,即依次将节点string的第i位变换(26次)然后在字典中查找变换后的节点是否在字典中(用的unordered_set数据结构,查找时间为常数),相同的话push进队列;
(4)节点不仅要记录string,还要记录其在遍历中是第几步,应该用一个struct封装;
好了上代码:
class Solution {
public:
struct Node{
string str;
int count;
Node(string s, int n) :str(s), count(n){}
};
public:
int ladderLength(string start, string end, unordered_set<string> &dict)
{
if (start == end)
return 1;
if (isNeibours(start, end))
return 2;
visited.clear();
while (!que.empty())
que.pop();
que.push(Node(start, 1));
int res = 0;
while (!que.empty())
{
Node NowNode(que.front());
if (isNeibours(NowNode.str, end))
{
res = NowNode.count + 1;
break;
}
que.pop();
for (int i = 0; i < NowNode.str.size(); ++i)
{
for (int j = 0; j < 26; ++j)
{
string temp = NowNode.str;
temp[i] = 'a' + j;
if (temp != NowNode.str && dict.find(temp) != dict.end() && visited.find(temp) == visited.end())
{
que.push(Node(temp,NowNode.count+1));
visited.insert(temp);
}
}
}
}
return res;
}
private:
bool isNeibours(string s1, string s2)
{
int count = 0;
for (int i = 0; i < s1.size(); ++i)
{
if (s1[i] != s2[i])
++count;
if (count > 1)
return false;
}
return true;
}
private:
unordered_set<string> visited;
queue<Node> que;
};