|
| 1 | +\chapter{广度优先搜索} |
| 2 | +当题目看不出任何规律,既不能用分治,贪心,也不能用动规时,这时候万能方法——搜索, |
| 3 | +就派上用场了。搜索分为广搜和深搜,广搜里面又有普通广搜,双向广搜,A*搜索等。 |
| 4 | +深搜里面又有普通深搜,回溯法等。 |
| 5 | + |
| 6 | +广搜和深搜非常类似(除了在扩展节点这部分不一样),二者有相同的框架,如何表示状态? |
| 7 | +如何扩展状态?如何判重?尤其是判重,解决了这个问题,基本上整个问题就解决了。 |
| 8 | + |
| 9 | + |
| 10 | +\section{Word Ladder} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 11 | +\label{sec:wordladder} |
| 12 | + |
| 13 | + |
| 14 | +\subsubsection{描述} |
| 15 | +Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that: |
| 16 | +\begindot |
| 17 | +\item Only one letter can be changed at a time |
| 18 | +\item Each intermediate word must exist in the dictionary |
| 19 | +\myenddot |
| 20 | + |
| 21 | +For example, Given: |
| 22 | + |
| 23 | +\begin{Code} |
| 24 | +start = "hit" |
| 25 | +end = "cog" |
| 26 | +dict = ["hot","dot","dog","lot","log"] |
| 27 | +\end{Code} |
| 28 | +As one shortest transformation is \code{"hit" -> "hot" -> "dot" -> "dog" -> "cog"}, return its length $5$. |
| 29 | + |
| 30 | +Note: |
| 31 | +\begindot |
| 32 | +\item Return 0 if there is no such transformation sequence. |
| 33 | +\item All words have the same length. |
| 34 | +\item All words contain only lowercase alphabetic characters. |
| 35 | +\myenddot |
| 36 | + |
| 37 | + |
| 38 | +\subsubsection{分析} |
| 39 | + |
| 40 | + |
| 41 | +\subsubsection{代码} |
| 42 | +\begin{Code} |
| 43 | +//LeetCode, Word Ladder |
| 44 | +class Solution { |
| 45 | +public: |
| 46 | + int ladderLength(string start, string end, unordered_set<string> &dict) { |
| 47 | + if (start.size() != end.size()) return 0; |
| 48 | + if (start.empty() || end.empty()) return 1; return 0; |
| 49 | + int level = 1; // 层次 |
| 50 | + queue<string> queToPush, queToPop; |
| 51 | + |
| 52 | + queToPop.push(start); |
| 53 | + while (dict.size() > 0 && !queToPop.empty()) { |
| 54 | + while (!queToPop.empty()) { |
| 55 | + string str(queToPop.front()); |
| 56 | + queToPop.pop(); |
| 57 | + for (int i = 0; i < str.size(); i++) { |
| 58 | + for (char j = 'a'; j <= 'z'; j++) { |
| 59 | + if (j == str[i]) continue; |
| 60 | + |
| 61 | + const char temp = str[i]; |
| 62 | + str[i] = j; |
| 63 | + if (str == end) return level + 1; //找到了 |
| 64 | + |
| 65 | + if (dict.count(str) > 0) { |
| 66 | + queToPush.push(str); |
| 67 | + dict.erase(str); // 删除该单词,防止死循环 |
| 68 | + } |
| 69 | + str[i] = temp; // 恢复该单词 |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + swap(queToPush, queToPop); //!!! 交换两个队列 |
| 74 | + level++; |
| 75 | + } |
| 76 | + return 0; // 所有单词已经用光,还是找不到通向目标单词的路径 |
| 77 | + } |
| 78 | +}; |
| 79 | +\end{Code} |
| 80 | + |
| 81 | + |
| 82 | +\subsubsection{相关题目} |
| 83 | + |
| 84 | +\begindot |
| 85 | +\item Word Ladder II,见 \S \ref{sec:wordladderii} |
| 86 | +\myenddot |
| 87 | + |
| 88 | + |
| 89 | +\section{Word Ladder II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 90 | +\label{sec:wordladderii} |
| 91 | + |
| 92 | + |
| 93 | +\subsubsection{描述} |
| 94 | +Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that: |
| 95 | +\begindot |
| 96 | +\item Only one letter can be changed at a time |
| 97 | +\item Each intermediate word must exist in the dictionary |
| 98 | +\myenddot |
| 99 | + |
| 100 | +For example, Given: |
| 101 | +\begin{Code} |
| 102 | +start = "hit" |
| 103 | +end = "cog" |
| 104 | +dict = ["hot","dot","dog","lot","log"] |
| 105 | +\end{Code} |
| 106 | +Return |
| 107 | +\begin{Code} |
| 108 | +[ |
| 109 | + ["hit","hot","dot","dog","cog"], |
| 110 | + ["hit","hot","lot","log","cog"] |
| 111 | +] |
| 112 | +\end{Code} |
| 113 | + |
| 114 | +Note: |
| 115 | +\begindot |
| 116 | +\item All words have the same length. |
| 117 | +\item All words contain only lowercase alphabetic characters. |
| 118 | +\myenddot |
| 119 | + |
| 120 | + |
| 121 | +\subsubsection{分析} |
| 122 | +跟 Word Ladder比,这题是求路径本身,不是路径长度,也是BFS,略微麻烦点 |
| 123 | + |
| 124 | + |
| 125 | +\subsubsection{代码} |
| 126 | + |
| 127 | +\begin{Code} |
| 128 | +//LeetCode, Word Ladder II |
| 129 | +//跟 Word Ladder比,这题是求路径本身,不是路径长度,也是BFS,略微麻烦点 |
| 130 | +class Solution { |
| 131 | +public: |
| 132 | + std::vector<std::vector<std::string> > findLadders(std::string start, |
| 133 | + std::string end, std::unordered_set<std::string> &dict) { |
| 134 | + result_.clear(); |
| 135 | + std::unordered_map<std::string, std::vector<std::string>> prevMap; |
| 136 | + |
| 137 | + for (auto iter = dict.begin(); iter != dict.end(); ++iter) { |
| 138 | + prevMap[*iter] = std::vector<std::string>(); |
| 139 | + } |
| 140 | + |
| 141 | + std::vector<std::unordered_set<std::string>> candidates(2); |
| 142 | + |
| 143 | + int current = 0; |
| 144 | + int previous = 1; |
| 145 | + |
| 146 | + candidates[current].insert(start); |
| 147 | + |
| 148 | + while (true) { |
| 149 | + current = !current; |
| 150 | + previous = !previous; |
| 151 | + |
| 152 | + // 从dict中删除previous中的单词,避免自己指向自己 |
| 153 | + for (auto iter = candidates[previous].begin(); |
| 154 | + iter != candidates[previous].end(); ++iter) { |
| 155 | + dict.erase(*iter); |
| 156 | + } |
| 157 | + |
| 158 | + candidates[current].clear(); |
| 159 | + |
| 160 | + for (auto iter = candidates[previous].begin(); |
| 161 | + iter != candidates[previous].end(); ++iter) { |
| 162 | + for (size_t pos = 0; pos < iter->size(); ++pos) { |
| 163 | + std::string word = *iter; |
| 164 | + for (int i = 'a'; i <= 'z'; ++i) { |
| 165 | + if (word[pos] == i) continue; |
| 166 | + |
| 167 | + word[pos] = i; |
| 168 | + |
| 169 | + if (dict.count(word) > 0) { |
| 170 | + prevMap[word].push_back(*iter); |
| 171 | + candidates[current].insert(word); |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + if (candidates[current].size() == 0) return result_; // 此题无解 |
| 178 | + if (candidates[current].count(end)) break; // 没看懂 |
| 179 | + } |
| 180 | + |
| 181 | + std::vector<std::string> path; |
| 182 | + GeneratePath(prevMap, path, end); |
| 183 | + |
| 184 | + return result_; |
| 185 | + } |
| 186 | + |
| 187 | +private: |
| 188 | + std::vector<std::vector<std::string>> result_; |
| 189 | + |
| 190 | + void GeneratePath( |
| 191 | + std::unordered_map<std::string, std::vector<std::string>> &prevMap, |
| 192 | + std::vector<std::string>& path, const std::string& word) { |
| 193 | + if (prevMap[word].size() == 0) { |
| 194 | + path.push_back(word); |
| 195 | + std::vector<std::string> curPath = path; |
| 196 | + reverse(curPath.begin(), curPath.end()); |
| 197 | + result_.push_back(curPath); |
| 198 | + path.pop_back(); |
| 199 | + return; |
| 200 | + } |
| 201 | + |
| 202 | + path.push_back(word); |
| 203 | + for (auto iter = prevMap[word].begin(); iter != prevMap[word].end(); |
| 204 | + ++iter) { |
| 205 | + GeneratePath(prevMap, path, *iter); |
| 206 | + } |
| 207 | + path.pop_back(); |
| 208 | + } |
| 209 | +}; |
| 210 | +\end{Code} |
| 211 | + |
| 212 | + |
| 213 | +\subsubsection{相关题目} |
| 214 | + |
| 215 | +\begindot |
| 216 | +\item Word Ladder,见 \S \ref{sec:wordladder} |
| 217 | +\myenddot |
0 commit comments