https://leetcode.com/problems/word-ladder/
https://leetcode.com/problems/word-ladder-ii/
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
从一个字符串按照每次变化一个char阶梯模式变化成目的字符串,其中中间所有的临时字符串都应该来自于给定的wordlist
class Solution {
public:
bool isOnlyOneWordDiff(string s1, string s2) //相同或者只有一个char不同
{
int count = 0;
for (int i=0;i<s1.size();i++)
{
if (s1[i] != s2[i])count++;
}
return count <= 1;
}
void __findLadders(string beginWord, string endWord, const vector<string> &wordVector,vector<bool>&used,vector<string>&tmp,vector<vector<string>>&res)
{
if (isOnlyOneWordDiff(beginWord, endWord))
{
tmp.push_back(endWord);
res.push_back(tmp);
tmp.pop_back();
return;
}
for (int i=0;i<wordVector.size();i++)
{
if (used[i])continue;
if (isOnlyOneWordDiff(beginWord, wordVector[i]))
{
used[i] = true;
tmp.push_back(wordVector[i]);
__findLadders(wordVector[i], endWord, wordVector, used, tmp, res);
tmp.pop_back();
used[i] = false;
}
}
}
vector<vector<string>> findLadders(string beginWord, string endWord, unordered_set<string> &wordList)
{
//所有的word是一样长
//用递归
vector<string>tmp;
vector<vector<string>>res;
vector<string>wordVector;
auto it1 = wordList.find(beginWord);
auto it2 = wordList.find(endWord);
if (it1 != wordList.end())wordList.erase(it1);//保证原string和目的string不会出现在string里面
if (it2 != wordList.end())wordList.erase(it2);
for (auto x:wordList)
{
wordVector.push_back(x); //转为vector形式,保证wordlist里面的只会出现一次
}
vector<bool>used(wordVector.size(), false);
tmp.push_back(beginWord);
__findLadders(beginWord, endWord, wordVector, used, tmp, res);//调用递归函数
if (res.size() < 2)return res; //处理最后结果,取得最短的子集
int len = res[0].size();
for (int i=1;i<res.size();i++)
{
len = std::min(len, (int)res[i].size());
}
vector<vector<string>>result;
for (int i=0;i<res.size();i++)
{
if (res[i].size() == len)
result.push_back(res[i]);
}
return result;
}
};
这种递归的做法,暴力枚举所有的可能情况,要么求出最短的长度,要么输出最短的,超时!Time Limited Exceeded
在word ladder1中:
BFS,广度搜索,每搜索一层,dist++;
class Solution_string_ladder
{
public:
int ladderLength(string beginWord, string endWord, unordered_set<string>& wordList) {
wordList.insert(endWord);
queue<string>toVisit;
addNextHopeString(beginWord, toVisit, wordList);
int dist = 2;
while (!toVisit.empty())
{
for (int i=0,n=toVisit.size();i<n;++i) //此处是最核心的,每次for访问完,相当于把当前string可以一步达到的全部访问完
{<span style="white-space:pre"> </span> //或者说,从beginWord经过dist-1步后能够访问的全部访问完
string s = toVisit.front();
toVisit.pop();
if (s == endWord)return dist;
addNextHopeString(s, toVisit, wordList);
}
dist++;
}
return 0;
}
void addNextHopeString(string s, queue<string>&toVisit, unordered_set<string>&wordList)
{
for (int i = 0;i < s.size();++i)
{
char c = s[i];
for (int j=0;j<26;++j)
{
s[i] = 'a' + j; //此处的技巧是把s一步变化后所有可能的情况全部枚举
if (wordList.find(s) != wordList.end())
{
toVisit.push(s);
wordList.erase(s); //wordlist里面的string可能有多种方式从beginWord访问到,此处保留最短的step方案
}
}
s[i] = c;
}
}
};
上面这种BFS
扩张级别可以看成:1->2->4->8->2^n,最后在2^n里面找到endWord
优化two-end BFS: 1->2->4->8->2^n || 2^n<-8<-4<-2<-1,在两个集合里面找到符合条件的。
具体算法:
int ladderLength(string beginWord, string endWord, unordered_set<string>& wordList)
{
wordList.erase(beginWord);
wordList.erase(endWord);
unordered_set<string>nextNei;
unordered_set<string>preNei;
int dist = 2;
int len = beginWord.size();
nextNei.insert(beginWord);
preNei.insert(endWord);
while (!nextNei.empty() && !preNei.empty())
{
if (nextNei.size() > preNei.size())
swap(nextNei, preNei); //swap函数取巧,交换了二者的内容
unordered_set<string>itNext;
for (auto s : nextNei)
{
for (int i = 0;i < len;i++)
{
char c = s[i];
for (int j = 0;j < 26;j++)
{
s[i] = 'a' + j;
if (preNei.find(s) != preNei.end())return dist;
if (wordList.find(s) != wordList.end())
{
wordList.erase(s);
itNext.insert(s);
}
}
s[i] = c;
}
}
dist++;
swap(nextNei, itNext); //同样是swap取巧
}
return 0;
}
第一步:BFS+DFS
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, unordered_set<string> &wordList)
{
wordList.insert(endWord); //加入搜索集合
unordered_map<string, vector<string>>stringWithLadder; //每一个string对应它ladder邻居
unordered_set<string>toVisit; //BFS每一层待访问的string
toVisit.insert(beginWord); //BFS初始
wordList.erase(beginWord); //搜索集合里面删除已经访问的
bool isMeet = false; //访问结束,使不进入下一层访问
while (isMeet==false&&!toVisit.empty()) //已经找到目的string,或者断层
{
unordered_set<string>nextNeigb; //当前访问层里面的每个string的ladder集合,也即是下一个待访问层
for (string s:toVisit) //对每一个元素寻找ladder
{
if (s == endWord)
{
isMeet = true; //如果这层能够访问到,那么就不需要下一层
}
else
{
if (isMeet == true)continue; //跳出,因为当前层可能有多种方案
string ssaved(s); //要访问的元素备份
for (int i = 0;i < s.size();++i)
{
char c = s[i];
for (char j = 'a';j <= 'z';++j)
{
if (j == c)continue;
s[i] = j; //两个for循环,把string所有的ladder全部找到
if (wordList.find(s) != wordList.end())//wordlist里面存储的都是还没有访问到string
{
nextNeigb.insert(s);
stringWithLadder[ssaved].push_back(s);
}
}
s[i] = c;
}
}
}
for (string t:nextNeigb)//删除所有访问的和已经确认接下来就访问的string
{
wordList.erase(t);
}
toVisit = nextNeigb;//得到下一层访问的string
}
vector<vector<string>>res;
if (isMeet == false)return res;
vector<string>path(1,beginWord); //DFS起始点
backTrackDFS( beginWord,endWord,stringWithLadder,path,res);
return res;
}
void backTrackDFS(string beginWord,string endWord, unordered_map<string, vector<string>>stringWithLadder, vector<string>&path,vector<vector<string>>&res)
{
if (endWord == beginWord)
{
res.push_back(path);
return;
}
for (string s:stringWithLadder[beginWord]) //当前string的所有ladder string
{
path.push_back(s);
backTrackDFS(s, endWord, stringWithLadder, path, res);
path.pop_back();
}
}
};
用two-end BFS+DFS
<span style="font-family:Microsoft YaHei;font-size:10px;">class Solution_string_ladder2 {
public:
std::vector<std::vector<std::string> > findLadders(std::string beginWord, std::string endWord, std::unordered_set<std::string> &dict) {
std::vector<std::vector<std::string> > paths;
std::vector<std::string> path(1, beginWord);
if (beginWord == endWord) {
paths.push_back(path);
return paths;
}
std::unordered_set<std::string> words1, words2;
words1.insert(beginWord); //BFS支线1
words2.insert(endWord); //BFS支线2
std::unordered_map<std::string, std::vector<std::string> > nexts;
bool words1IsBegin = false; //选择哪个支线
if (findLaddersHelper(words1, words2, dict, nexts, words1IsBegin)) //如果能够找到,那么调用DFS
getPath(beginWord, endWord, nexts, path, paths);
return paths;
}
private:
bool findLaddersHelper( //传入参数都是引用
std::unordered_set<std::string> &words1,
std::unordered_set<std::string> &words2,
std::unordered_set<std::string> &dict,
std::unordered_map<std::string, std::vector<std::string> > &nexts,
bool &words1IsBegin) {
words1IsBegin = !words1IsBegin;
if (words1.empty()) //只要有一个BFS支线出现断层,那么都不可能访问到
return false; //这也是递归的截止条件
if (words1.size() > words2.size())
return findLaddersHelper(words2, words1, dict, nexts, words1IsBegin);
for (auto it = words1.begin(); it != words1.end(); ++it)
dict.erase(*it); //已经出现的点就不在访问
for (auto it = words2.begin(); it != words2.end(); ++it)
dict.erase(*it);
std::unordered_set<std::string> words3; //待访问的下一层
bool reach = false;
for (auto it = words1.begin(); it != words1.end(); ++it) {
std::string word = *it;
for (auto ch = word.begin(); ch != word.end(); ++ch) {
char tmp = *ch; //word发生改变ladder,当前点是it*
for (*ch = 'a'; *ch <= 'z'; ++(*ch))
if (*ch != tmp)
if (words2.find(word) != words2.end()) {
reach = true;
words1IsBegin ? nexts[*it].push_back(word) : nexts[word].push_back(*it); //建立一座桥梁,把最后的两层连接起来
}
else if (!reach && dict.find(word) != dict.end()) {
wordfhs3.insert(word);
words1IsBegin ? nexts[*it].push_back(word) : nexts[word].push_back(*it);
}
*ch = tmp;
}
}
return reach || findLaddersHelper(words2, words3, dict, nexts, words1IsBegin); //使用递归
}
void getPath(
std::string beginWord,
std::string &endWord,
std::unordered_map<std::string, std::vector<std::string> > &nexts,
std::vector<std::string> &path,
std::vector<std::vector<std::string> > &paths) {
if (beginWord == endWord)
paths.push_back(path);
else
for (auto it = nexts[beginWord].begin(); it != nexts[beginWord].end(); ++it) {
path.push_back(*it);
getPath(*it, endWord, nexts, path, paths);
path.pop_back();
}
}
};</span>