---
# System prepended metadata

title: 'LeetCode #126 [Word Ladder II](https://leetcode.com/problems/word-ladder-ii/)'
tags: [DFS, LeetCode, Hard, BFS, Queue]

---

###### 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();
        }
    }
};
```