【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,而另一道Word Ladder被标注为难度3。事实上在做的过程中我感觉Word Ladder II的难度比Word Ladder要高。解题的关键在于选取适当的数据结构,题目要求输出所有路径,不像Word Ladder里一样只需要输出最少路径节点数。因此不是在该层找到第一个可达节点就能返回,而是要访问所有可达节点,并且记录下节点之间的关系。一个节点可能经过多个下一层节点到达目的节点,也就是说一个节点可能对应多个下一层节点,同时,可能有多个节点经过字符变换以后到达下一层的某一个节点,因此一个节点可能与多个上一层节点相对应起来。因此考虑采用multimap来建立节点之间的关系。从起点开始采用BFS搜索,路径上会经过许多无效路径(不是最短路径的路径或不可达路径),因此如果当前节点与下一层节点的关系,就会有许多干扰关系的存在。注意到与目的节点关联的节点总是有效节点,因此在建立节点关系时,采用将当前节点与上一层节点关联的方法,这样一来我们最后就可以从终点出发,倒推出各条有效最短路径。multimap的键为当前节点,值为与当前节点关联的上一层节点。

        另一方面,由于采用BFS,可以采用队列来存储待访问的节点。然而,在访问节点时需要保证访问的节点不在当前层次和上一层次的已有节点中(前者会导致重复访问,后者会导致反复循环),需要对节点进行剪枝。对于前一个问题,可以设置一个visited标志,如果已经访问过,就不将该节点继续压入待访问队列。对于后一个问题,可以设一个变量来记录当前的层次数,当访问到下一个层次时,将上一层次的所有节点从字典里面清空。

        通过以上的步骤和细节处理,就可以建立起从目的节点到初始节点的一副有向无环图。这时只需要用DFS算法将各条路径记录下来即可,需要注意到从目的节点开始DFS所得到的结果是反向的路径,对于vector容器,可以用reverse函数将其反转。此外,虽然存储节点信息的数据结构在DFS搜索时不需要做改变,可以采用传值调用,然而由于在节点较多时其存储的数据比较大,此时采用传值调用时进行形参复制的时间代价较高,采用传引用会大幅度的缩减运算时间(采用传值调用的程序运行了1692ms,传引用的程序只需要运行748ms)。下面给出代码。


代码:

class Solution {
public:
    vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
		vector<vector<string>> Path;
		vector<string> CurrPath;
		unordered_set<string> visited;
		queue<pair<string,int>> CurrCandidate;
		multimap<string,string> father;
		bool found=false;
		int curr_step=0;
		int WordSize=start.size();
		if(start.empty()||end.empty())return Path;
		if(start==end){
			CurrPath.push_back(start);
			Path.push_back(CurrPath);
		}
		if(dict.count(start))dict.erase(start);
		if(dict.count(end))dict.erase(end);
		CurrCandidate.push(make_pair(start,1));
		visited.insert(start);
		while(!CurrCandidate.empty()){
			pair<string,int> CurrWord(CurrCandidate.front());
			CurrCandidate.pop();
			if(curr_step<CurrWord.second){
			    if(found)break;
			    unordered_set<string>::iterator iter;
				for(iter=visited.begin();iter!=visited.end();iter++){
					dict.erase(*iter);
				}
				curr_step=CurrWord.second;
				visited.clear();
			}
			for(int i=0;i<WordSize;i++){
				for(char c='a';c<='z';c++){
				    if(c==CurrWord.first[i])continue;
					swap(c,CurrWord.first[i]);
					if(CurrWord.first==end){
						found=true;
						string temp=CurrWord.first;
						swap(c,CurrWord.first[i]);
						father.insert(make_pair(end,CurrWord.first));
						break;
					}
					if(!found&&dict.count(CurrWord.first)){
						if(visited.count(CurrWord.first)==0){
							CurrCandidate.push(make_pair(CurrWord.first,CurrWord.second+1));
							visited.insert(CurrWord.first);
						}
						string temp=CurrWord.first;
						swap(c,CurrWord.first[i]);
						father.insert(make_pair(temp,CurrWord.first));
						continue;
					}
					swap(c,CurrWord.first[i]);
				}
				
			}
		}
		if(found==true){
			build_path(father,start,end,CurrPath,Path);
		}
		return Path;
    }

private:
	void build_path(multimap<string,string> &father,string start,string end,vector<string> &CurrPath,vector<vector<string>> &Path){
		CurrPath.push_back(end);
		if(start==end){
			Path.push_back(CurrPath);
			reverse(Path.back().begin(),Path.back().end());
			CurrPath.pop_back();
			return;
		}
		pair<multimap<string,string>::iterator,multimap<string,string>::iterator> pos=father.equal_range(end);
		while(pos.first!=pos.second){
			build_path(father,start,pos.first->second,CurrPath,Path);
			pos.first++;
		}
		CurrPath.pop_back();
	}
};


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值