leetcode:string hard系列二:word ladder

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;
	}


接下来,解决word ladder II

第一步: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>






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值