126单词接龙 II(深度优先搜索+广度优先搜索、广度优先搜索——困难)

1、题目描述

给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:

  • 每次转换只能改变一个字母。
  • 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回一个空列表。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

2、示例

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出:
[ ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"] ]

3、题解

问题抽象成把所有单词看成结点,相差一个字符的两个单词就是相邻的节点,给定起始节点和终止节点,找最短路径。本质上就是在正常的遍历的基础上,去剪枝,从而提高速度,如果使用简单的递归DFS,当数据达到一定程度超时。

  • 剪枝思想(核心思想):在考虑第 k 层的某一个单词,如果这个单词在第 1 到 k-1 层已经出现过,我们其实就不用继续向下探索了,该序列路径一定不是最短转换序列。

解法一:DFS+剪枝(剪枝方法:BFS、map、set)

利用BFS层次遍历建立起当前层每个单词与相邻的下一层单词的映射表HashMap,且这些相邻的单词在之前层没有出现过(这也是剪枝的思想,为了防止在DFS的时候搜索多余无用的结果),为了记录这些相邻的单词在之前层没有出现过,使用mindepth建立单词与最先出现的层数之间的映射,为了查找当前单词cur相邻的下一层单词,建立wordList的set表dict,改变当前单词cur的每一位字符从a到z,改变之后在dict里面查找是否存在,这种方法查找当前单词cur相邻的下一层单词相比于遍历wordList每个单词时间复杂度小很多,尤其是在wordList很大的时候。另外使用unordered_map和unordered_set而不是map和set,否则超时。

  • bfs层次遍历,构建HashMap用于保存与当前单词cur相邻的所有单词的映射表
  • 先把该层的所有单词记录深度,这时mindepth记录的才是单词最先出现的层数
  • 遍历当前层所有单词,记录每个单词相邻的在之前层没有出现过所有单词
  • 查找当前单词cur的下一个转换的单词:和cur只相差一个字母
  • 考虑第k层的某一个单词,如果这个单词在第1到k-1层已经出现过,就不用继续向下探索
  • dfs查找所有转换单词序列情况
  • 递归终止条件:当前单词cur就是目标单词
  • 当前单词为cur,利用HashMap映射表查找下一个转换的单词

解法二:BFS

解法一中 DFS 借助了 BFS 把所有的邻接关系保存了起来,再用 DFS 进行深度搜索。我们也可以只用 BFS,一边进行层次遍历,一边就保存转换单词序列路径。当到达结束单词的时候,就把当前转换单词序列路径存储到最后的返回结果res,省去再进行 DFS 的过程。BFS 的队列就不去存储 string 了,直接去存到目前为止的路径,也就是一个vector<string>。和解法一还有一点不同的是在HashMap里面出现过的单词cur还是要查找cur的下一个转换的单词,因为这是查找所有可能的转换序列,不同于解法一只是为了得到HashMap。

解法三:双向BFS搜索

#include<iostream>
#include<vector>
#include<algorithm>
#include<unordered_map>
#include<list>
#include<unordered_set>
using namespace std;
class Solution {
public:
	unordered_map<string, vector<string>> HashMap;    //HashMap用于保存与当前单词cur相邻的所有单词的映射表
	vector<vector<string>> res;    //res保存所有转换单词序列情况
	vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
		//基本思想:DFS+剪枝(剪枝方法:BFS、map、set)
		vector<string> ans;    //ans保存某一种转换单词序列情况
		ans.push_back(beginWord);
		bfs(beginWord, endWord, wordList);    //bfs构建HashMap
		dfs(endWord, ans, wordList);    //dfs查找所有转换单词序列情况
		return res;
	}
	void bfs(string beginWord, string endWord, vector<string>& wordList)
	{
		int depth = 0, flag = 0;
		list<string> queue;    //queue用于层次遍历
		unordered_map<string, int> mindepth;    //mindepth用于记录当前单词cur最先出现的层数
		unordered_set<string> dict(wordList.begin(), wordList.end());    //空间换时间,用于查找cur相邻的单词
		queue.push_front(beginWord);
		//层次遍历,构建HashMap用于保存与当前单词cur相邻的所有单词的映射表
		while (!queue.empty())
		{
			int len = queue.size();
			depth++;
			//先把该层的所有单词记录深度,这时mindepth记录的才是单词最先出现的层数
			for (auto i=queue.begin();i!=queue.end();i++)
			{
				if (mindepth.find(*i) == mindepth.end())
					mindepth[*i] = depth;
			}
			//遍历当前层所有单词,记录每个单词相邻的在之前层没有出现过所有单词
			for (int i = 0; i < len; i++)
			{
				string cur = queue.back();
				queue.pop_back();
				if (HashMap.find(cur) == HashMap.end())
				{
					//查找当前单词cur的下一个转换的单词:和cur只相差一个字母
					for (int j = 0; j < cur.size(); j++)
					{
						for (char c = 'a'; c <= 'z'; c++)
						{
							string temp = cur;
							if(cur[j]==c)
								continue;
							char oldc = cur[j];
							cur[j] = c;
							if (dict.find(cur) != dict.end()&&mindepth.find(cur) == mindepth.end())
							{
								//考虑第k层的某一个单词,如果这个单词在第1到k-1层已经出现过,就不用继续向下探索
						    	HashMap[temp].push_back(cur);
								queue.push_front(cur);
							}
							cur[j] = oldc;
						}
					}
				}
			}
		}
	}
	void dfs(string endWord, vector<string> ans, vector<string>& wordList)
	{
		string cur = ans[ans.size() - 1];
		//递归终止条件:当前单词cur就是目标单词
		if (cur == endWord)
		{
			res.push_back(ans);
			return;
		}
		//当前单词为cur,利用HashMap映射表查找下一个转换的单词
		for (auto v : HashMap[cur])
		{
			ans.push_back(v);
			dfs(endWord, ans, wordList);
			ans.pop_back();
		}
		return;
	}
};
class Solution1 {
public:
	unordered_map<string, vector<string>> HashMap;    //HashMap用于保存与当前单词cur相邻的所有单词的映射表
	vector<vector<string>> res;    //res保存所有转换单词序列情况
	vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
		//基本思想:BFS
		bfs(beginWord, endWord, wordList);
		return res;
	}
	void bfs(string beginWord, string endWord, vector<string>& wordList)
	{
		int depth = 0, flag = 0;
		list<vector<string>> queue;    //queue用于层次遍历
		vector<string> ans;    //ans保存某一种转换单词序列情况
		ans.push_back(beginWord);
		unordered_map<string, int> mindepth;    //mindepth用于记录当前单词cur最先出现的层数
		unordered_set<string> dict(wordList.begin(), wordList.end());    //空间换时间,用于查找cur相邻的单词
		queue.push_front(ans);
		//层次遍历,构建HashMap用于保存与当前单词cur相邻的所有单词的映射表
		while (!queue.empty())
		{
			int len = queue.size();
			depth++;
			//先把该层的所有单词记录深度,这时mindepth记录的才是单词最先出现的层数
			for (auto i = queue.begin(); i != queue.end(); i++)
			{
				if (mindepth.find((*i).back()) == mindepth.end())
					mindepth[(*i).back()] = depth;
			}
			//遍历当前层所有单词,记录每个单词相邻的在之前层没有出现过所有单词
			for (int i = 0; i < len; i++)
			{
				vector<string> ans = queue.back();
				string cur = ans.back();
				queue.pop_back();
				if (cur == endWord)
				{
					res.push_back(ans);
					flag = 1;
				}
				//查找当前单词cur的下一个转换的单词:和cur只相差一个字母
				for (int j = 0; j < cur.size(); j++)
				{
					for (char c = 'a'; c <= 'z'; c++)
					{
						string temp = cur;
						if (cur[j] == c)
							continue;
						char oldc = cur[j];
						cur[j] = c;
						if (dict.find(cur) != dict.end())
						{
							//考虑第k层的某一个单词,如果这个单词在第1到k-1层已经出现过,就不用继续向下探索
							if (mindepth.find(cur) == mindepth.end())
							{
								HashMap[temp].push_back(cur);
								ans.push_back(cur);
								queue.push_front(ans);
								ans.pop_back();
							}
						}
						cur[j] = oldc;
					}
			    }
			}
			if (flag == 1)
				break;
		}
	}
};
class Solution2 {
public:
	//保存所有转换单词序列情况
	vector<vector<string>> res;
	//最短的转换单词序列的长度
	int minlength=INT_MAX;
	vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
		//基本思想:单纯递归DFS,超时
		vector<string> ans;    //ans保存某一种转换单词序列情况
		ans.push_back(beginWord);
		Recursion(endWord, ans, wordList);
		return res;
	}
	void Recursion(string endWord, vector<string> ans, vector<string>& wordList)
	{
		//剪枝效果不佳,因为如果终点单词在比较深的层,那么dfs的规模仍然很庞大
		if (ans.size() > minlength)
			return;
		string cur = ans[ans.size() - 1];
		//递归终止条件:当前单词cur就是目标单词
		if (cur == endWord)
		{
			//只保留最短的转换单词序列
			if (res.empty())
			{
				res.push_back(ans);
				minlength = ans.size();
			}
			else
			{
				if (ans.size() < minlength)
				{
					res.clear();
					res.push_back(ans);
					minlength = ans.size();
				}
				else if (ans.size() == minlength)
				{
					res.push_back(ans);
				}
			}
			return;
		}
		//当前单词为cur,查找下一个转换的单词
		for (int i = 0; i < wordList.size(); i++)
		{
			int cnt = 0, flag = 0;
			//下一个转换的单词的要求:1、和当前单词cur只相差一个字母 2、还没在转换单词序列保存器ans中出现过
			//判断下一个转换的单词是否与当前单词cur只相差一个字母
			for (int j = 0; j < cur.size(); j++)
			{
				if (cur[j] == wordList[i][j])
					cnt++;
			}
			if (cnt == cur.size() - 1)
			{
				//判断下一个转换的单词是否已经在ans中出现
				for (auto& s : ans)
				{
					if (s == wordList[i])
					{
						flag = 1;
						break;
					}	
				}
				if(flag==1)
					continue;
				else
				{
					ans.push_back(wordList[i]);
					Recursion(endWord, ans, wordList);
					ans.pop_back();
				}
			}	
		}
		return;
	}
};
int main()
{
	Solution1 solute;
	string beginWord = "red";
	string endWord = "tax";
	vector<string> wordList = { "ted","tex","red","tax","tad","den","rex","pee" };
	vector<vector<string>> res = solute.findLadders(beginWord, endWord, wordList);
	cout << endl;
	cout << endl;
	cout << endl;
	for (int i = 0; i < res.size(); i++)
	{
		for_each(res[i].begin(), res[i].end(), [](const string& s) {cout << s << " "; });
		cout << endl;
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值