leetcode Word Ladder II

鉴于该题改了一晚上加一白天,我决定写篇博客纪念纪念。

Word Ladder II

My Submissions
Total Accepted: 33992  Total Submissions: 255558  Difficulty: Hard

Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the word list

For example,

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

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.


首先说说我的思路:
我想着从endword开始进行广度优先遍历,直到在某一层的遍历中遇到beginword。
此处广度优先遍历我采用双队列,一个队列是当前队列,一个是下一层的队列。
因为要构造查询单词的路劲,所以我使用一个map<string,vector<string>>来存放一个单词的先驱。这个地方为什么是 vector<string>呢?因为一个单词不是只有一个前驱。
还要创建一个unordered_map<string, int> strState来表示每一个节点是否被访问过,之所以还有第二个Int元素是因为可能存在上一层两个以上节点指向下一层同一个节点。距离来说,比如上一层的节点是"hot","dot"下一层“lot”的前驱就有两个,那么我上一层处理到hot时候,搜索到下一层要处理的节点是lot,同时lot的前驱是hot。同时当处理dot的时候,下一层的lot与dot只有一个字母不同,但是它标志访问过了,此时有了unordered_map<string, int> 中int,就可以知道该单词的前驱与搜索到的前驱是不是在同一层,在就把当前的作为前驱加入到
map<string,vector<string>>的vector<string>中去。

前驱举例:





则map<sting,vector>的存储样子是,

(dog,cog)

(log,cog)

(dot,dog)

(lot,log)

(hot,{dot,lot})

(hit,hot)

那么构造这个返回结果就简单了,看代码直接可以懂得

class Solution {
public:
  vector<vector<string>> findLadders(string beginWord, string endWord, unordered_set<string> &wordList)
{
	if (beginWord == endWord)
		return{};
	int n = wordList.size();
	wordList.insert(beginWord);//把开始的单词插入到单词set中。
	unordered_map<string, vector<string>> fatherString;//记录所有单词的前驱
	queue<string> currentque, nextque;//两个队列,一个是遍历的时候当前层的,一个是下一层要遍历的。
	unordered_map<string, int> strState;
	//状态哈希,如果遍历过则保存进来,注意第二个值表示它所在层的。可能上一层两个节点指向下一层同一个节点
	currentque.push(endWord);//首先将末尾压入当前处理的队列中
	string temp;
	static int level = 1;//记录现在处理第几层单词呢。
	int signal = 0;//表示下一层是否搜索到beginword.
	while (signal == 0 && !currentque.empty())
	{
		while (!currentque.empty())
		{
			temp = currentque.front();
			currentque.pop();

			for (int i = 0; i < temp.size(); i++)//搜索所有与temp差距一个字母的单词
			{
				string thestr = temp;
				for (char ch = 'a'; ch <= 'z'; ch++)
				{
					thestr[i] = ch;
					if (wordList.find(thestr) != wordList.end() && thestr!=temp)//查询这个与temp一个字母不一样的字符在不在wordlist中
					{
						if (strState.find(thestr) == strState.end())//在wordlist中,且没有被访问过
						{
							fatherString[thestr].push_back(temp);//记录前驱
							strState[thestr] = level;//表示被访问过了,并且是在第level层被访问的
							nextque.push(thestr);//将其加入的下一层遍历的节点中
							if (thestr == beginWord)//遇到了beginword,这层就是最后一层。
								signal = 1;
						}
						else if (level == strState[thestr])//在wordlist中,但是被访问过,需要查询前驱与当前的是不是一层。
							fatherString[thestr].push_back(temp);//在同一层,则temp也是该字符的前驱
					}
				}
			}
		}
		level++;//一层遍历完了,下一层
		std::swap(currentque, nextque);//此时currentque为空了,nextque存放下一层待遍历的,所以交换。
	}
	if (signal == 0)//表示遍历了所有元素,但是没发现beginword
		return{};
	else
	{
		return buildPath(fatherString, beginWord, endWord);//根据前驱构造结果。
	}
}

vector<vector<string>> buildPath(unordered_map<string, vector<string>> father, string beginWord, string endWord)
{
	vector<vector<string>> res;
	vector<vector<string>> restemp;
	vector<string> temp;
	string pre;
	int signal = 0;//表示构造到了endword.
	//首先将beginword的前驱到beginword的所有可能构造出来.
	for (int i = 0; i < father[beginWord].size(); i++)
	{
		temp.push_back(beginWord);
		temp.push_back(father[beginWord][i]);
		if (father[beginWord][i] == endWord)
			signal = 1;
		res.push_back(temp);
		temp.clear();
	}
	while (signal == 0)
	{
		for (int i = 0; i < res.size(); i++)
		{
			temp = res[i];
			for (int j = 0; j < father[res[i][res[i].size() - 1]].size(); j++)
			{
				pre = father[res[i][res[i].size() - 1]][j];
				if (pre == endWord)
					signal = 1;
				temp.push_back(pre);
				restemp.push_back(temp);
				temp.pop_back();
			}
		}
		res = restemp;
		restemp.clear();
	}
	return res;
}
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值