Leetcode: Word Ladder II

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

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

For example,

Given:
start = "hit"
end = "cog"
dict = ["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.

解法1:

      进行广度优先遍历,我们可以在发现替换字母后的单词在字典中时,不马上继续处理它,而是将其放入一个队列中。通过队列先进先出的性质,就可以实现广度优先的处理了。由于最后要输出的是整个转换序列,为了简单起见,我们可以将当前已转换出的序列放入队列中,即队列形式为queue<vector<std::string>>,序列的最后一个元素就是下次处理时要继续转换的单词。

使用广度优先遍历后,还有一个特性使得我们可以更方便的处理深度优先中重复单词的问题。当一个单词在某一层(一层即从第一个单词到当前单词的长度一样的序列)出现后,后面再出现的情况肯定不会是最短序列(相当于走了回头路),因此我们可以在处理完一层后直接将已用过的单词从字典中去除。需要注意的是,同一层是不能去除的,如例子中的hot在两个序列的第二层中都出现了。这样我们就需要一个容器把当前层用过的单词保存起来,等处理的层数发生变化时再将它们从字典里移除。

最后要注意的是查找结束的条件。由于找到了一个序列后该序列只是最短的之一,我们还要继续进行处理,直到队列中当前层都处理完毕。所以我们要在找到一个序列后将它的长度记录下来,当要处理的序列长度已经大于该值时,就可以结束查找了(代码中用字符串所在的层数作为长度)。

vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict)
{
	queue<vector<string>> candidate;
	vector<vector<string>> result;
	vector<string> usedWord;

	int prePathLen=0;
	int shortest = 0;

	vector<string> vec;
	vec.push_back(start);
	candidate.push(vec);
	usedWord.push_back(start);

	bool foundShortest=false;

	while (!candidate.empty())
	{
		string curStr = *candidate.front().rbegin();

		if (candidate.front().size()!=prePathLen)
		{
			for (int k=0;k<usedWord.size();k++)
			{
				dict.erase(usedWord[k]);
			}
		}


		if (shortest<prePathLen && foundShortest)
		{
			break;
		}


		for (int i=0;i<curStr.size();i++)
		{
			string tmp = curStr;
			for (char ch='a';ch<='z';ch++)
			{
				tmp[i] = ch;

				if (dict.find(tmp)!=dict.end())
				{
					usedWord.push_back(tmp);
				}
				

				if (tmp == end)
				{
					result.push_back(candidate.front());
					result.rbegin()->push_back(end);
					foundShortest = true;
					shortest = result.rbegin()->size();
				}

				if (dict.find(tmp)!=dict.end())
				{					
					vector<string> newPath=candidate.front();
					newPath.push_back(tmp);
					candidate.push(newPath);
				}

			}
		}
		
		prePathLen = candidate.front().size();
		candidate.pop();

	}

	return result;
}


解法2:

    使用一个前驱单词表,即记录每一个单词的前驱单词是哪些。这样在遍历完毕后,我们从end出发递归就能把所有路径生成出来。但是由于前驱单词表不能记录当前的层次信息,似乎我们没法完成去重的工作。这个方案的巧妙之处就在于它没有使用我们通常的队列保存待处理的单词,一个单词一个单词先进先出处理的方法,而是使用两个vector来模拟队列的操作。我们从vector 1中遍历单词进行转换尝试,发现能转换的单词后将其放入vector 2中。当vector 1中的单词处理完毕后即为一层处理完毕,它里面的单词就可以从字典里删去了。
    
vector<vector<string>> findLadders2(string start, string end, unordered_set<string> &dict)  
{  


	unordered_map<string, vector<string>> prevMap;  
	for(auto iter = dict.begin(); iter != dict.end(); ++iter)  
		prevMap[*iter] = vector<string>(); 

	prevMap[start] = vector<string>();
	prevMap[end] = vector<string>();

	vector<unordered_set<string>> candidates(2);  
	int current = 0;  
	int previous = 1;  
	candidates[current].insert(start);  
	while(true)  
	{  
		current = !current;  
		previous = !previous;  
		for (auto iter = candidates[previous].begin(); iter != candidates[previous].end(); ++iter)  
			dict.erase(*iter);  

	        candidates[current].clear();

		for(auto iter = candidates[previous].begin(); iter != candidates[previous].end(); ++iter)  
		{  
			for(size_t pos = 0; pos < iter->size(); ++pos)  
			{  
				string word = *iter;  
				for(char ch = 'a'; ch <= 'z'; ++ch)  
				{  
					if(word[pos] == ch)
						continue;

					word[pos] = ch;  

					if (word == end)
					{					
						prevMap[word].push_back(*iter);  
						candidates[current].insert(word);
					}

					if(dict.count(word) > 0)  
					{  
						prevMap[word].push_back(*iter);  
						candidates[current].insert(word);  
					}  
				}  
			}  
		}  

		if (candidates[current].count(end)) 
			break;
	}  
		
	vector<string> path;
	vector<vector<string>> result;
	GeneratePath(prevMap, path, end,result);
	
	return result;
}  


void GeneratePath(unordered_map<string, vector<string>> &prevMap, vector<string> path, string word, vector<vector<string>>& result)  
{  
	  if (prevMap[word].size()==0)
	  {
		  path.push_back(word);
		  result.push_back(path);
		  reverse(path.begin(),path.end());
		  return;
	  }


	 path.push_back(word);

	 for (int i=0;i<prevMap[word].size();i++)
	 {	 
		 GeneratePath(prevMap, path, prevMap[word][i], result);
	 }

}  



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值