leecode 解题总结:126. Word Ladder II

#include <iostream>
#include <stdio.h>
#include <vector>
#include <unordered_map>
#include <string>
#include <map>
#include <queue>
using namespace std;
/*
问题:
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

Only one letter can be changed at a time
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
For example,

Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
Note:
Return an empty list if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
UPDATE (2017/1/20):
The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.

分析:这是程序员面试金典的一道题目。
给定两个字符串和一个单词列表,每次只能变换一个字符,求从初始字符串变换到结束字符串变换次数最少的
序列,如果序列有多个,请全部返回。如果不存在这样的变换序列,返回空列表。

采用暴力破解:对初始字符串的每个字符进行从'a'到'z'的变换,如果变换后的字符串在变换列表中继续尝试变换下一个字符。
否则说明当前变换的字符不符合,就先变换下一个字符
这个应该是回溯,一旦当前字符摆放不成功,要回溯到原来的状态
递归,并保存一个当前最短序列的值,如果当前摆放转换次数已经大于最少序列的值,直接放弃这一转换,回退到顶点继续

输入:
hit(起始单词) cog(结束单词) 6(单词数组元素个数)
hot dot dog lot log cog
hit cog 5
hot dot dog lot log
hot dog 3
hot dog dot
输出:
hit hot dot dog cog, hit hot lot log cog
no result
hot dot dog

超时:回溯主要是对每个字符都尝试进行变换来获取下一个回溯的字符串,这个很慢
程序员面试金典的做法:建一个map<string, vector<string> >,其中键是某个单词,
值是这个单词只变换一个字符的所有可能下的下一个字符串列表
采用队列+获取当前单词,对当前单词所有可能的只需要一次字符变换的数组继续遍历
还是超时了。


应该是我提前把所有的变位词计算出来后,耗时过长,如果每次遇到新的变位词,才去计算,并将计算结果保存,不就行了吗?
还是超时

关键:
1 参考这位的解法:https://leetcode.com/problems/word-ladder-ii/?tab=Solutions
另外一种解法是找到一条最短的距离(通过bfs)minDis,并且在遍历过程中存储任意字符串到初始字符串的距离。
那么dfs搜索的时候采用当前结点的下一个结点遍历的时候需要满足下一个结点距离初始结点的距离=当前结点距离初始结点的距离+1
否则,就剪枝,不递归。
*/

class Solution {
public:
	//获取某个单词的所有变位词
	vector<string> getOneEditWords(string& beginWord , vector<string>& wordList, unordered_map<string ,int>& words,unordered_map<string , vector<string> >& wordToNextWords)
	{
		//如果已经计算过了,就直接返回
		if(1 == wordToNextWords.count(beginWord))
		{
			return  wordToNextWords[beginWord];
		}
		//求出开始单词的可经过一次变换的所有字符串
		vector<string> begNextWords;
		char temp;
		int len;
		if(!beginWord.empty())
		{
			len = beginWord.length();
			for(int i = 0; i < len ; i++)
			{
				temp = beginWord.at(i);
				for(char ch = 'a' ;  ch <= 'z' ; ch++)
				{
					if(temp == ch)
					{
						continue;
					}
					beginWord.at(i) = ch;
					//如果新的单词,在单词列表中存在,就加入结果集
					if(1 == words.count(beginWord))
					{
						begNextWords.push_back(beginWord);
					}
					beginWord.at(i) = temp;
				}
			}
			wordToNextWords[beginWord] = begNextWords;
		}
		return begNextWords;
	}

	//获取所有字符串距离初始字符串的距离映射,以及最少变换次数
	int bfs(string& beginWord, string& endWord, vector<string>& wordList,
		unordered_map<string , vector<string>>& wordToNextWords , 
		unordered_map<string,int>& words, unordered_map<string,int>& strToDistance)
	{
		queue<string> queueWords;
		queueWords.push(beginWord);
		strToDistance[beginWord] = 0;//初始字符串到自身距离为0
		string word;
		string nextWord;
		vector<string> nextWords;
		int curDis = 0;
		int size;
		unordered_map<string ,int> visited;
		bool isFound = false;
		while(!queueWords.empty())
		{
			word = queueWords.front();
			queueWords.pop();
			nextWords = getOneEditWords(word , wordList, words , wordToNextWords);
			if(nextWords.empty())
			{
				continue;
			}
			//当前距离是从映射中获取的
			curDis = -1;
			if(strToDistance.find(word) != strToDistance.end())
			{
				curDis = strToDistance[word];
			}
			size = nextWords.size();
			for(int i = 0 ; i < size ; i++)
			{
				nextWord = nextWords.at(i);
				//当前结点必须是未访问过的,不行有些结点需要用到之前访问过的
				//设置当前结点距离初始结点的距离
				if(strToDistance.find(nextWord) == strToDistance.end())
				{
					strToDistance[ nextWord] = curDis + 1;
					//判断当前字符串是否等于结束字符串,就直接退出
					if(nextWord == endWord)
					{
						isFound = true;
						//break;//不能直接退出,当前层的其他元素需要设置该距离
					}
					//将当前字符串压入到队列中
					queueWords.push(nextWord);
				}
			}
			if(isFound)
			{
				break;
			}
		}
		if(isFound)
		{
			return curDis;//这个距离是倒数第二个结点距离初始结点的距离,实际距离=curDis+1
		}
		else
		{
			return -1;
		}
	}

	void dfs(string& beginWord, string& endWord,unordered_map<string,int>& words, vector<string> result,
		vector< vector<string> >& results,unordered_map<string , vector<string>>& wordToNextWords , 
		vector<string>& wordList, unordered_map<string,int>& strToDistance)
	{
		result.push_back(beginWord);
		//找到了,返回
		if(beginWord == endWord)
		{
			results.push_back(result);
			return;
		}
		vector<string> nextWords = getOneEditWords(beginWord , wordList  , words , wordToNextWords);
		if(nextWords.empty())
		{
			return ;
		}
		int size = nextWords.size();
		string nextWord;
		for(int i = 0 ; i < size ; i++)
		{
			nextWord = nextWords.at(i);
			//牛逼,剪枝,将下一个结点集合中所有与当前结点到初始结点距离+1 不等的全部过滤
			if(strToDistance[nextWord] == strToDistance[beginWord] + 1)
			{
				dfs(nextWord, endWord, words , result , results, wordToNextWords, wordList , strToDistance);
			}
		}
	}

    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        vector<vector<string>> results;
		if(beginWord.empty() || endWord.empty() || wordList.empty())
		{
			return results;
		}
		vector<string> result;
		int size = wordList.size();
		unordered_map<string ,int> words;
		for(int i =  0; i < size ; i++)
		{
			words[ wordList.at(i) ] = 1;
		}
		int minSequence = INT_MAX;
		unordered_map<string ,int> visited;
		unordered_map<string , vector<string>> wordToNextWords; //= getOneEditMap(beginWord , wordList , words);
		unordered_map<string , int> strToDistance;
		bfs(beginWord , endWord, wordList , wordToNextWords, words ,strToDistance);
		dfs(beginWord, endWord, words, result , results , wordToNextWords , wordList , strToDistance);
		return results;
    }
};

void print(vector<vector<string> >& result)
{
	if(result.empty())
	{
		cout << "no result" << endl;
		return;
	}
	int size = result.size();
	int len;
	for(int i = 0 ; i < size ; i++)
	{
		len = result.at(i).size();
		for(int j = 0 ; j < len ; j++)
		{
			cout << result.at(i).at(j) << " " ;
		}
		cout << ",";
	}
	cout << endl;
}

void process()
{
	 vector<string> nums;
	 string value;
	 int num;
	 Solution solution;
	 string beginWord;
	 string endWord;
	 vector<vector<string> > result;
	 while(cin >> beginWord >> endWord >> num )
	 {
		 nums.clear();
		 for(int i = 0 ; i < num ; i++)
		 {
			 cin >> value;
			 nums.push_back(value);
		 }
		 result = solution.findLadders(beginWord , endWord , nums);
		 print(result);
	 }
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}


/*
	void dfs2(string& beginWord, string& endWord,unordered_map<string,int>& words, vector<string>& result, 
		vector< vector<string> >& results, int& minSequenceLen, unordered_map<string ,int>& visited,
		unordered_map<string , vector<string>>& wordToNextWords , vector<string>& wordList)
	{
		//结果集中字符串个数 >= 最小序列长度,肯定不可能,返回false
		if(!result.empty() && result.size() > minSequenceLen )
		{
			return;
		}
		//找到一份结果
		if(!result.empty() && result.at( result.size() - 1 ) == endWord)
		{
			//更新最短序列的值,当前记录的结果长度比原先还要短,则原先的全部清除
			if(result.size() < minSequenceLen)
			{
				results.clear();
				minSequenceLen = result.size();
			}
			results.push_back(result);
			return;
		}
		//接下来获取每个字符串的下一个字符串
		//unordered_map<string, vector<string> >::iterator it = wordToNextWords.find(beginWord);
		//如果找不到,就计算
		vector<string> nextWords = getOneEditWords(beginWord , wordList  , words , wordToNextWords);
		if(nextWords.empty())
		{
			return ;
		}
		int size = nextWords.size();
		string nextWord;
		for(int i = 0 ; i < size ; i++)
		{
			nextWord = nextWords.at(i);
			//如果找到当前变换的字符,就继续递归向下寻找,并把当前字符串插入到其中,必须区分已经存在的字符串
			//if(1 == words.count(nextWord) && 0 == visited.count(nextWord))//字典中存在,并且是未被访问过的
			if(0 == visited.count(nextWord))//字典中存在,并且是未被访问过的
			{
				result.push_back(nextWord);
				//设置访问标记
				visited[nextWord] = 1;
				dfs2(nextWord , endWord, words , result, results, minSequenceLen ,visited , wordToNextWords , wordList);
				//回溯
				result.pop_back();
				visited.erase(nextWord);
			}
		}
	}


	//获取每个单词经过一个字符变换对应的字符串列表,估计是计算这个表耗时了
	unordered_map<string , vector<string>> getOneEditMap(string beginWord , vector<string>& wordList, unordered_map<string ,int>& words)
	{
		unordered_map<string , vector<string> > wordToNextWords;
		//求出开始单词的可经过一次变换的所有字符串
		vector<string> begNextWords;
		char temp;
		int len;
		if(!beginWord.empty())
		{
			len = beginWord.length();
			for(int i = 0; i < len ; i++)
			{
				temp = beginWord.at(i);
				for(char ch = 'a' ;  ch <= 'z' ; ch++)
				{
					if(temp == ch)
					{
						continue;
					}
					beginWord.at(i) = ch;
					//如果新的单词,在单词列表中存在,就加入结果集
					if(1 == words.count(beginWord))
					{
						begNextWords.push_back(beginWord);
					}
					beginWord.at(i) = temp;
				}
			}
			wordToNextWords[beginWord] = begNextWords;
		}
		int size = wordList.size();
		for(int j = 0 ; j < size ; j++ )
		{
			vector<string> nextWords;
			beginWord = wordList.at(j);
			len = beginWord.length();
			for(int i = 0; i < len ; i++)
			{
				temp = beginWord.at(i);
				for(char ch = 'a' ;  ch <= 'z' ; ch++)
				{
					if(temp == ch)
					{
						continue;
					}
					beginWord.at(i) = ch;
					//如果新的单词,在单词列表中存在,就加入结果集
					if(1 == words.count(beginWord))
					{
						nextWords.push_back(beginWord);
					}
					beginWord.at(i) = temp;
				}
			}
			wordToNextWords[beginWord] = nextWords;
		}
		return wordToNextWords;
	}

	//用广度优先搜索来做
	void bfs(string& beginWord, string& endWord,unordered_map<string,int>& words,
		unordered_map<string , vector<string>>& wordToNextWords)
	{
		vector<string> result;
		vector< vector<string> > results;
		int minSequenceLen = INT_MAX;
		unordered_map<string ,int> visited;
		queue<string> queueWords;
		queueWords.push(beginWord);
		string word;
		vector<string> nextWords;
		string nextWord;
		int size;
		while(!queueWords.empty())
		{
			word = queueWords.front();
			queueWords.pop();
			//寻找下一个单词
			if(0 == wordToNextWords.count(word))
			{
				continue;
			}
			nextWords = wordToNextWords[word];
			if(nextWords.empty())
			{
				continue;
			}
			size = nextWords.size();
			for(int i = 0 ; i < size ; i++)
			{
				nextWord = nextWords.at(i);
				//如果当前单词已经访问过了
				if(1 == visited.count(nextWord))
				{
					continue;
				}
				//没有访问过,将当前单词压入队列
			}
		}
	}


超时正确版本
class Solution {
public:
	void dfs(string beginWord, string& endWord,unordered_map<string,int> words, vector<string>& result, 
		vector< vector<string> >& results, int& minSequenceLen, unordered_map<string ,int>& visited)
	{
		//结果集中字符串个数 >= 最小序列长度,肯定不可能,返回false
		if(!result.empty() && result.size() > minSequenceLen )
		{
			return;
		}
		//找到一份结果
		if(!result.empty() && result.at( result.size() - 1 ) == endWord)
		{
			//更新最短序列的值,当前记录的结果长度比原先还要短,则原先的全部清除
			if(result.size() < minSequenceLen)
			{
				results.clear();
				minSequenceLen = result.size();
			}
			results.push_back(result);
			return;
		}
		//接下来尝试变换字符
		int len = beginWord.length();
		char temp;
		for(int i = 0 ; i < len ; i++)
		{
			//对位置i上进行尝试,
			temp = beginWord.at(i);
			for(char ch = 'a' ; ch <= 'z' ; ch++)
			{
				//跳过当前字符本身
				if(temp == ch)
				{
					continue;
				}
				beginWord.at(i) = ch;
				//如果找到当前变换的字符,就继续递归向下寻找,并把当前字符串插入到其中,必须区分已经存在的字符串
				if(1 == words.count(beginWord) && 0 == visited.count(beginWord))//字典中存在,并且是未被访问过的
				{
					result.push_back(beginWord);
					//设置访问标记
					visited[beginWord] = 1;
					dfs(beginWord , endWord, words , result, results, minSequenceLen ,visited);
					//回溯
					result.pop_back();
					visited.erase(beginWord);
				}
				//回溯,重新设定为原来状态
				beginWord.at(i) = temp;
			}
		}
	}

    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        vector<vector<string>> results;
		if(beginWord.empty() || endWord.empty() || wordList.empty())
		{
			return results;
		}
		vector<string> result;
		result.push_back(beginWord);
		int size = wordList.size();
		unordered_map<string ,int> words;
		for(int i =  0; i < size ; i++)
		{
			words[ wordList.at(i) ] = 1;
		}
		int minSequence = INT_MAX;
		unordered_map<string ,int> visited;
		dfs(beginWord, endWord, words, result , results , minSequence , visited);
		return results;
    }
};
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值