Leetcode 127. Word Ladder

Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence 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.
Note:

Return 0 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.
Example 1:

Input:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]

Output: 5

Explanation: As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.

method 1 BFS

標準的BFS遍歷代碼,並且使用一個set記錄已經單詞列表中的單詞,只有改變後的單詞在這個範圍內,才能壓進隊列

int ladderLength3(string beginWord, string endWord, vector<string>& wordList) {
	unordered_set<string> dirc(wordList.begin(), wordList.end());
	queue<string> q;
	q.push(beginWord);

	int cnt = 1;
	while (!q.empty()){
		int sz = q.size();
		while (sz-- > 0){
			string p = q.front(); q.pop();
			
			if (p == endWord) return cnt;

			dirc.erase(p);
			for (int i = 0; i < p.size(); i++)
			{
				char c = p[i];
				for (int k = 0; k < 26; k++){
					p[i] = 'a' + k;
					if (dirc.count(p)) {
						q.push(p);
					}
				}
				p[i] = c;
			}
		}

		cnt++;
	}

	return 0;
}

以下是第一版BFS代碼,但超時了,因爲在計算每個單詞可以改變成的其他單詞映射時,浪費了大量時間,與之相比,上面的代碼只是每個單詞的每個字符從‘a’-'z’依次改變,運算量相對小一點

bool canChange(string from, string to){
	int diff = 0;
	for (int i = 0; i < from.size(); i++){
		if (from[i] != to[i]) diff++;
		if (diff == 2) return false;
	}

	return true;
}

void fillLadder(string beginWord, vector<string>& wordList, unordered_map<string, vector<string>>& ladder){
	for (int i = 0; i < wordList.size(); i++)
	{
		if ( beginWord != wordList[i] && canChange(beginWord, wordList[i]))
			ladder[beginWord].push_back(wordList[i]);
	}

}

int ladderLength2(string beginWord, string endWord, vector<string>& wordList) {
	if (find(wordList.begin(), wordList.end(), endWord) == wordList.end()) return 0;

	unordered_map<string, vector<string>> ladder;
	fillLadder(beginWord, wordList, ladder);
	for (int i = 0; i < wordList.size(); i++)
		if(wordList[i] != endWord) fillLadder(wordList[i], wordList, ladder);

	queue<string> q;
	q.push(beginWord);

	int cnt = 1;
	while (!q.empty()){
		int sz = q.size();
		while (sz-- > 0){
			string p = q.front(); q.pop();
			if (ladder.count(p)){
				vector<string> changeList = ladder[p];
				for (int i = 0; i < changeList.size(); i++)
				{
					if (changeList[i] == endWord) return cnt + 1;
					else if(ladder.count(changeList[i])) q.push(changeList[i]);
				}

				ladder.erase(p);
			}
		}

		cnt++;
	}

	return 0;
}

method 2 Bidirectional Breadth First Search

以下爲雙端寬度優先搜索原理的解釋:
The idea behind bidirectional search is to run two simultaneous searches—one forward from
the initial state and the other backward from the goal—hoping that the two searches meet in
the middle.The motivation is that b ^ (d / 2) + b ^ (d / 2) is much less than b^d.b is branch factor, d is depth. "

---- - section 3.4.6 in Artificial Intelligence - A modern approach by Stuart Russel and Peter Norvig

以下是雙端優先搜索減少搜索量的示意圖

具體操作中,

  1. 使用兩個set,一個beginSet,一個endSet,每次總是從beignSet開始,所以每次循環開始,總是置大的set爲beginSet
  2. 終止的條件是從beginSet開始的遍歷中,當改變後的單詞出現在endSet中時,即代表可以成功轉變
  3. 總共轉變的次數即層數
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
	if(find(wordList.begin(), wordList.end(),endWord) == wordList.end()) return 0;

    unordered_set<string> beginSet, endSet, visitedSet, dirc(wordList.begin(), wordList.end());
	beginSet.insert(beginWord);
	endSet.insert(endWord);

	int cnt = 1; 
	while (!beginSet.empty() && !endSet.empty()){
		if (beginSet.size() > endSet.size()){
			unordered_set<string> tmp = beginSet;
			beginSet = endSet;
			endSet = tmp;
		}

		unordered_set<string> tmp;
		for (string word : beginSet){
			for (int i = 0; i < word.size(); i++){
				char c = word[i];
				for (int k = 0; k < 26; k++){
					word[i] = 'a' + k;
					if (endSet.count(word)) return cnt + 1;
					if (!visitedSet.count(word) && dirc.count(word)){
						visitedSet.insert(word);
						tmp.insert(word);
					}
				}
				word[i] = c;
			}
		}

		cnt++;
		beginSet = tmp;
	}

	return 0;

}

summary

  1. BFS標準模板
  2. 對於BFS的問題,如果可以同時從兩端開始遍歷,並且他們都可以獨立進行,那麼使用Bidirectional Breadth First Search
發佈了93 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章