给定一个单词集合Dict,其中每个单词的长度都相同。现从此单词集合Dict中抽取两个单词A、B,我们希望通过若干次操作把单词A变成单词B,每次操作可以改变单词的一个字母,同时,新产生的单词必须是在给定的单词集合Dict中。求所有行得通步数最少的修改方法。
举个例子如下:
Given:
A = "hit"
B = "cog"
Dict = ["hot","dot","dog","lot","log"]
Return
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
即把字符串A = "hit"转变成字符串B = "cog",有以下两种可能:
"hit" -> "hot" -> "dot" -> "dog" -> "cog";
"hit" -> "hot" -> "lot" -> "log" ->"cog"。
class Solution {
public:
vector<vector<string> > findLadders(string start, string end,
set<string>& dict) {
vector<vector<string> > result, result_temp;
if (dict.erase(start) == 1 && dict.erase(end) == 1) {
// 为当前方向上所有点与其下一结点的配对
map<string, vector<string> > kids_from_start;
map<string, vector<string> > kids_from_end;
//开始方向经过的点集
set<string> reach_start;
reach_start.insert(start);
//终点方向经过的点集
set<string> reach_end;
reach_end.insert(end);
set<string> meet;
//BFS直到相交
while (meet.empty() && !reach_start.empty() && !reach_end.empty()) {
if (reach_start.size() < reach_end.size()) {
//从开始方向搜索子结点和其分支
search_next_reach(reach_start, reach_end, meet,
kids_from_start, dict);
/*cout << "start ";
printMap(kids_from_start);*/
} else {
search_next_reach(reach_end, reach_start, meet,
kids_from_end, dict);
/* cout << "end ";
printMap(kids_from_end);*/
}
// printset(meet);
}
if (!meet.empty()) {
//由相遇点拓展可走路径数
for (set<string>::iterator it = meet.begin(); it != meet.end();
++it) {
//参数1是空间,参数2是值
vector<string> words(1, *it);
result.push_back(words);
}
//由相遇点向起点拓展分支
walk(result, kids_from_start);
/*printvv(result);
printMap(kids_from_start);*/
for (size_t i = 0; i < result.size(); ++i) {
reverse(result[i].begin(), result[i].end());
}
walk(result, kids_from_end);
/*printvv(result);
printMap(kids_from_end);*/
}
}
return result;
}
private:
/*reach为该方向已经经过的点集,
* other_reach为反方向,
* meet为两个方向点集相交点集,
* path为当前方向上所有点与其下一结点的配对,(下一元素:{当前元素})
* dict为字典集合*/
void search_next_reach(set<string>& reach, const set<string>& other_reach,
set<string>& meet, map<string, vector<string> >& path,
set<string>& dict) {
//reach必须从端点开始遍历,所以必须重新添加,不然会重复
set<string> temp;
reach.swap(temp);
/*对该方向已出现的元素匹配下一元素
* 匹配方案:
* 尝试依次改变指定位置上的元素来匹配*/
for (set<string>::iterator it = temp.begin(); it != temp.end(); ++it) {
string s = *it;
for (size_t i = 0; i < s.length(); ++i) {
char back = s[i];
for (s[i] = 'a'; s[i] <= 'z'; ++s[i]) {
if (s[i] != back) {
//已出现在当前点集
if (reach.count(s) == 1) {
path[s].push_back(*it);
} else if (dict.erase(s) == 1) { //存在于字典中,删除后加入点集
path[s].push_back(*it);
reach.insert(s);
} else if (other_reach.count(s) == 1) { //存在于相反方向集合,说明点集相交
path[s].push_back(*it);
reach.insert(s);
meet.insert(s);
}
}
}
s[i] = back;
}
}
}
void walk(vector<vector<string> >& all_path,
map<string, vector<string> > kids) {
vector<vector<string> > temp;
//到达起止点则为空
while (!kids[all_path.back().back()].empty()) {
//每个路径的结尾元素可以对应多个子元素,则拼接路径有多条,所以必须先清空再添加
all_path.swap(temp);
all_path.clear();
for (vector<vector<string> >::iterator it = temp.begin();
it != temp.end(); ++it) {
vector<string>& one_path = *it;
//取得当前路径尾元素的下一元素集合
vector<string>& p = kids[one_path.back()];
//将下一元素集合的每一元素与尾元素分别拼接,存为新路径
for (size_t i = 0; i < p.size(); ++i) {
all_path.push_back(one_path);
all_path.back().push_back(p[i]);
}
}
}
}
};
本文介绍了一种算法,用于寻找两个单词间的最短转换路径。给定初始单词、目标单词及单词字典,通过最少步骤将初始单词转换为目标单词,每步仅改变一个字符且中间结果必须在字典中。采用双向广度优先搜索策略,提高了搜索效率。
1844

被折叠的 条评论
为什么被折叠?



