###### tags: `LeetCode` `BFS` `Hard` `DFS` `Queue` # LeetCode #126 [Word Ladder II](https://leetcode.com/problems/word-ladder-ii/) ### (Hard-真的難) 按字典 wordList 完成從單詞 beginWord 到單詞 endWord 轉化,一個表示此過程的 轉換序列 是形式上像 beginWord -> s1 -> s2 -> ... -> sk 這樣的單詞序列,並滿足: 每對相鄰的單詞之間僅有單個字母不同。 轉換過程中的每個單詞 si(1 <= i <= k)必須是字典 wordList 中的單詞。注意,beginWord 不必是字典 wordList 中的單詞。 sk == endWord 給你兩個單詞 beginWord 和 endWord ,以及一個字典 wordList 。請你找出並返回所有從 beginWord 到 endWord 的 最短轉換序列 ,如果不存在這樣的轉換序列,返回一個空列表。每個序列都應該以單詞列表 [beginWord, s1, s2, ..., sk] 的形式返回。 --- 與 [#127](https://hackmd.io/Cz2A4hKcT9SOkJn0QBEx7w)有些類似, 但差別在需要回傳全部的最短路徑, 而非找到最短路徑長後就能結束。 為了(1)找到最短路徑以及(2)找到所有最短路徑組合, 需要建立一個資料結構儲存節點的父子關係以及該點的層級(到達該點的最短路徑長), 兩者皆可用無序哈希表unordered_map儲存。 此外, 使用bool found紀錄是否完成路徑探索, 若已找到, 則該輪探索完後就可以停止探索了(因為即使後面的探索找到終點, 仍然不是最短路徑)。 BFS的探索方式大同小異, 但要注意的的是由於路徑上的字串可能會被多個路徑經過, 因此不能隨意修改, 需要複製字串(元)後, 修改複製的字串(元)進行BFS搜尋。 註1:由於steps[s]在經過BFS搜尋後, 必定儲存到s點的最小值, 因此, 只有步數差距與steps[s]為1的單字才有資格成為s的parent(才有資格成為最短路徑的其中一個節點)。 在建完parent表後, 再透過DFS往回建立最短路徑組合。 由endWord開始, 遍歷每一個parent直到找到beginWord, 此時將暫存數組tmp存入答案數組中ans, 此外, 由於遍歷順序是從endWord -> beginWord, 因此在存入答案數組時, 需使用反轉指標(rbegin(), rend())。 註2:dict的功用是避免插入重複的parents, 導致最後的答案有重複值。 --- ``` class Solution { public: vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) { unordered_set<string> dict(wordList.begin(), wordList.end()); if(!dict.count(endWord))return {}; dict.erase(beginWord); dict.erase(endWord); queue<string> q; unordered_map<string, int> steps; steps[beginWord]=1; unordered_map<string, vector<string>> parents; q.push(beginWord); const int l = beginWord.length(); bool found = false; int step = 0; vector<vector<string>> ans; while(!q.empty()&&!found){ ++step; for(int i=q.size();i>0;i--){ const string str = q.front();q.pop(); string s = str; for(int j = 0; j<l;j++){ const char c = s[j]; for(char k = 'a'; k<='z';k++){ if(k==c)continue; s[j]=k; if(s==endWord){ parents[s].push_back(str); found = true; } else{ //註1 if(steps.count(s)&&step==steps.at(s)-1) parents[s].push_back(str); } if(dict.count(s)){ dict.erase(s); steps[s]=steps.at(str)+1; parents[s].push_back(str); q.push(s); } } s[j]=c; } } } if(found){ vector<string> tmp{endWord}; dfs(endWord, beginWord, parents, tmp, ans); } return ans; } void dfs(const string& word, const string& beginWord, const unordered_map<string, vector<string>>& parents, vector<string>& tmp, vector<vector<string>>& ans){ if(word==beginWord){ ans.push_back(vector<string>(tmp.rbegin(), tmp.rend())); return; } for(auto s:parents.at(word)){ tmp.push_back(s); dfs(s, beginWord, parents, tmp, ans); tmp.pop_back(); } } }; ```