leetcode 随笔 Word Ladder II -- BFS DFS

Word Ladder II

今天这个题做了好几天呀,感觉不难理解,就是代码疯狂超时,最后决定还是先看看Word Ladder

Word Ladder

此题的主要思路是利用BFS,我们把这一堆字符串看成一个个的节点,这里我们不需要真把这一个个节点构造成图,只需要记录他们就可以了。

思路:

比如我们第一层的字符串是beginWord,第二层字符串是所有beginWord能够到达的位置

第二层字符串构造好之后,第三层是所有第二层字符串能够到达的字符串

直到某一层我们看到了endWord,结束

层数即为所需要走的步数。

注意点:

1.字符串的移动具有可逆性,比如“hot”可以到“dot”,“dot”也可以到“hot”,所以我们需要利用hash记录字符串的访问情况,避免出现环路。

2.如何判断下一层的字符串?

这里有两种思路,一是遍历所有没有访问过的字符串,然后一个一个与当前字符串进行比较,事实证明--超时

二就是直接遍历字符串的所有字符,让字符串的每个字符进行26次替换得到新的字符串,然后再利用hash与没有访问过的字符串进行映射,看似很麻烦,实际上是可行的。

class Solution {
public:
	vector<string> record;
	unordered_set<string> pack;
	int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
		for (int i = 0; i < wordList.size(); i++)
		{
			pack.insert(wordList[i]);
		}
		if (pack.find(endWord)==pack.end()) return 0;
		record.push_back(beginWord);
		int res = 0;
		while (!record.empty())
		{
			res++;
			vector<string> next;
			for (int i = 0; i < record.size(); i++)
			{
				pack.erase(record[i]);
				if (record[i] == endWord)
				{
					return res;
				}
				else
				{
					for (int j = 0; j < record[i].size(); j++)
					{
						string n = record[i];
						for (int c = 'a'; c <= 'z'; c++)
						{
							n[j] = c;
							if (pack.find(n)!=pack.end())
							{
								next.push_back(n);
								pack.erase(n);
							}
						}
					}
				}
			}
			record = next;
		}
		return 0;
	}
};

上面的代码的runtime是84ms,那么还有没有能优化的地方呢?

其实不难发现,如果wordlist相当巨大,那么每一层的大小是快速膨胀的,层越大,需要进行的字符替换操作就越多,时间也就越长。

如果我们把问题反过来,从endword作为一层,反推到beginword,问题的规模其实是一样的,但是如果我们从两头开始推,每次都选规模最小的层往下推,直到两头“对接”了,那么问题结束,时间上也会有很大程度上优化。

假设从beginword开始第一层推出的第二层有500个字符串,这个时候同样是走一层,从endword往前走这一层只有1个字符串,而从第二层则有500个。

优化代码如下:

class Solution {
public:
	unordered_set<string> record;
	unordered_set<string> record2;
	int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
		unordered_set<string> pack(wordList.begin(), wordList.end());
		if (pack.find(endWord)==pack.end()) return 0;
		record.insert(beginWord);
		record2.insert(endWord);
		pack.erase(beginWord);
		pack.erase(endWord);
		int res = 1;
		while (!record.empty())
		{
			res++;
			unordered_set<string> next;
			for (string s:record)
			{
				for (int j = 0; j < s.size(); j++)
				{
					string n = s;
					for (int c = 'a'; c <= 'z'; c++)
					{
						n[j] = c;
						if (record2.find(n) != record2.end())
						{
							return res;
						}
						if (pack.find(n)!=pack.end())
						{
							next.insert(n);
							pack.erase(n);
						}
					}
				}
			}
			record = next.size() > record2.size() ? record2 : next;
			record2 = next.size() > record2.size() ? next : record2;
		}
		return 0;
	}
};

优化过代码的runtime就达到了34ms

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

下面我们来看Word Ladder II

不光是层数了,要你具体求出路径

Word Ladder的基础上,我的思路是这样的,BFS已经不能完全解决问题了,还得需要DFS,DFS的话我只对树或者图比较熟悉,所以这次就需要真的把图建立出来。

图节点数据结构如下:nb是下一层的节点集

	struct node
	{
		string val;
		vector<node*> nb;
		node(string s) :val(s) {};
	};

图的构造是主要麻烦的地方,dfs很简单,bfs就利用上一题的思路就行

图的构造也用到了hash

注意点:

1.可能有同一层的两个不同节点同时指向下一层同一节点

2.可能存在同一层某一节点可以指向本层另一节点,这样是不被允许的,因为白白增加路径长度

3.所有的节点都应提前构造好,访问的时候直接拿来用,如果new一个新节点容易出现问题

class Solution {
public:
	struct node
	{
		string val;
		vector<node*> nb;
		node(string s) :val(s) {};
	};
	vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
		unordered_set<string> record(wordList.begin(), wordList.end());
		unordered_map<string, node*> noderec;
		for (int i = 0; i < wordList.size(); i++)
		{
			noderec.insert(std::make_pair(wordList[i], new node(wordList[i])));
		}
		unordered_set<node*> next;
		vector<vector<string>> res;
		noderec[beginWord] = new node(beginWord);
		node* ld = noderec[beginWord];
		if (record.find(endWord) == record.end()) return res;
		record.erase(beginWord);
		//record.erase(endWord);
		vector<string> rec;
		rec.push_back(beginWord);
		next.insert(ld);
		while (!next.empty())
		{
			unordered_set<node*> tmp;
			for (node* s : next)
			{
				record.erase(s->val);
				if (s->val == endWord)
				{
					tmp.clear();
					break;
				}
				for (int i = 0; i < s->val.size(); i++)
				{
					string n = s->val;
					for (int j = 'a'; j <= 'z'; j++)
					{
						n[i] = j;
						if (record.find(n) != record.end()&&next.find(noderec[n])==next.end())
						{
							s->nb.push_back(noderec[n]);
							if(tmp.find(noderec[n])==tmp.end())
								tmp.insert(noderec[n]);
						}
					}
				}
			}
			next = tmp;
		}
		vector<string> local;
		local.push_back(beginWord);
		dfs(res, noderec[beginWord], endWord, local);
		return res;
	}
	void dfs(vector<vector<string>> &res, node *root,string &es,vector<string> &local)
	{
		if (!root) return;
		if (root->val == es)
		{
			res.push_back(local);
			return;
		}
		for (int i = 0; i < root->nb.size(); i++)
		{
			local.push_back(root->nb[i]->val);
			dfs(res, root->nb[i], es, local);
			local.pop_back();
		}
	}
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值