# [LeetCode 126. Word Ladder II ](https://leetcode.com/explore/challenge/card/july-leetcoding-challenge-2021/611/week-4-july-22nd-july-28th/3825/) ### Daily challenge Jul 24, 2021 (HARD) >A **transformation sequence** from word `beginWord` to word `endWord` using a dictionary `wordList` is a sequence of words `beginWord -> s1 -> s2 -> ... -> sk` such that: > >* Every adjacent pair of words differs by a single letter. >* Every `si` for `1 <= i <= k` is in `wordList`. Note that `beginWord` does not need to be in `wordList`. >* `sk == endWord` > >Given two words, beginWord and endWord, and a dictionary wordList, return all the **shortest transformation sequences** from beginWord to endWord, or an empty list if no such sequence exists. Each sequence should be returned as a list of the words `[beginWord, s1, s2, ..., sk]`. :::info **Example 1:** **Input:** beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] **Output:** [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]] **Explanation:** There are 2 shortest transformation sequences: "hit" -> "hot" -> "dot" -> "dog" -> "cog" "hit" -> "hot" -> "lot" -> "log" -> "cog" ::: :::info **Example 2:** **Input:** beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] **Output:** [] **Explanation:** The endWord "cog" is not in wordList, therefore there is no valid transformation sequence. ::: :::warning **Constraints:** * 1 <= beginWord.length <= 5 * endWord.length == beginWord.length * 1 <= wordList.length <= 1000 * wordList[i].length == beginWord.length * beginWord, endWord, and wordList[i] consist of lowercase English letters. * beginWord != endWord * All the words in wordList are unique. ::: --- ### Approach 1 : BFS + DFS :book: **`12 ms ( 79.62% )`** **`O(NK^2 + α)`** >`Here N is the number of words in wordList, K is the maximum length of a word, α is the number of possible paths from beginWord to endWord in the directed graph we have.` * **`unordered_map<string, vector<string>> possibleSequence`** ---> 紀錄 `[beginWord][adjacent words]` 表示所有 `beginWord` 能走到的下一個 `Word`。 * **BFS** : >* **`queue<string> q`** ---> 紀錄 `BFS`。 **`unordered_set<string> used`** ---> 紀錄已使用過的 `word`。 **`vector<string> thisLayer`** ---> 紀錄當前 `BFS` 使用到的 `adjacent words`。 >1. 首先找出 `adjacent words`。 >---> **`vector<string> nextStep = findNext(current, copyWorldList)`**。 >2. 儲存到 **`possibleSequence[current].push_back(NEXT)`**。 >3. 若 `NEXT` 之前無使用過 ---> `push` 到 `queue` 中。 >4. 最後根據 **`thisLayer`** 將用過的 `word` 從 **`wordList`** 中移除,可減少搜尋時間以及**避免重複找到相同的 `adjacent words`**。 * **findNext()** : > 將 `word` 第 `i` 個元素依序替換成 `a ~ z`,再從 **`wordList`** 中判斷是否存在。 * **DFS** : >1. 根據 **`possibleSequence`** 使用遞迴找到答案。 >2. **`vector<string> Path`** 紀錄當前路徑。 >3. 當 **`current == target`** 時,**`Path`** 就會是其中一個答案。 --- ![](https://i.imgur.com/C13eMJI.png) :::spoiler **詳細步驟** ### BFS : * **`queue = ["red"]`、 `wordList = ["ted","tex","tax","tad","den","rex","pee"]`**。 ( **`startWord`** 需先從 **`wordList`** 中移除 ) >1. `current = "red"`。 `nextStep = ["ted", "rex"]`。 **`possibleSequence[red] = {"ted", "rex"}`**。 ---> `queue = ["ted", "rex"]`。 * **`queue = ["ted", "rex"]`、 `wordList = ["tex","tax","tad","den","pee"]`**。 >1. `current = "ted"`。 `nextStep = ["tad", "tex"]`。 **`possibleSequence[ted] = {"tad", "tex"}`**。 --->`queue = ["rex", "tad", "tex"]`。 >2. `current = "rex"`。 `nextStep = ["tex"]`。 ( `"tex"` 已使用過 )。 **`possibleSequence[rex] = {"tex"}`**。 --->`queue = ["tad", "tex"]`。 * **`queue = ["tad", "tex"]`、 `wordList = ["tax","den","pee"]`**。 >1. `current = "tad"`。 `nextStep = ["tax"]`。 **`possibleSequence[tad] = {"tax"}`**。 --->`queue = ["tex", "tax"]`。 >2. `current = "tex"`。 `nextStep = ["tax"]`。 **`possibleSequence[tex] = {"tax"}`**。 --->`queue = ["tax"]`。 * **`queue = ["tax"]`、 `wordList = ["den","pee"]`**。 > no `adjacent words`。 > ---> queue = []。 > ---> end BFS。 > --- ### DFS : **1.** >* `current = "red"`、`target = "tax"`、**`Path = {"red"`}**。 >**`possibleSequence[red] = {"ted", "rex"}`**。 **2.** >* `current = "ted"`、`target = "tax"`、**`Path = {"red", "ted"}`**。 >**`possibleSequence[ted] = {"tad", "tex"}`**。 >* `current = "rex"`、`target = "tax"`、**`Path = {"red", "rex"}`**。 >**`possibleSequence[rex] = {"tex"}`**。 **3.** >* `current = "tad"`、`target = "tax"`、**`Path = {"red", "ted", "tad"}`**。 >**`possibleSequence[tad] = {"tax"}`**。 > `current = "tex"`、`target = "tax"`、**`Path = {"red", "ted", "tex"}`**。 >**`possibleSequence[tex] = {"tax"}`**。 >* `current = "tex"`、`target = "tax"`、**`Path = {"red", "rex", "tex"}`**。 >**`possibleSequence[tex] = {"tax"}`**。 **4.** `current = "tax"`、`target = "tax"`。 >**Find three answer !!!** >---> **`ans[0] = Path = {"red", "ted", "tad", "tax"}`** >---> **`ans[1] = Path = {"red", "ted", "tex", "tax"}`** >---> **`ans[2] = Path = {"red", "rex", "tex", "tax"}`** ::: --- :::success :warning: :warning: :warning: ```cpp=1 // faster ---> 12 ms ( 79.62% ) vector<string> findNext(string &word, unordered_set<string>& copyWorldList) { vector<string> neighbor; for (int i = 0; i < word.size(); i++) { char oldChar = word[i]; for (char c = 'a'; c <= 'z'; c++) { word[i] = c; if (c == oldChar || copyWorldList.find(word) == copyWorldList.end()) continue; neighbor.push_back(word); } word[i] = oldChar; } return neighbor; } ``` This version is slower. I think it's because **`string temp = current`** needs to copy `current` to `temp` which waste a lot of time. Also **`if(next != original && copyWorldList.find(temp) != copyWorldList.end())`** is slower than above version. ```cpp= // slower ---> 52 ms ( 16.38% ) vector<string> findNext(string current, unordered_set<string> copyWorldList){ vector<string> neighbor; for(int i=current.length()-1; i>=0; i--){ char original = current[i]; string temp = current; for(char next = 'a'; next<='z'; next++){ temp[i] = next; if(next != original && copyWorldList.find(temp) != copyWorldList.end()){ neighbor.push_back(temp); } } } return neighbor; } ``` ::: --- ```cpp= class Solution { public: unordered_map<string, vector<string>> possibleSequence; // [start], [all the adjacent words] vector<vector<string>> ans; // find all the adjacent words vector<string> findNext(string &word, unordered_set<string>& copyWorldList) { vector<string> neighbors; for (int i = 0; i < word.size(); i++) { char original = word[i]; for (char next='a'; next<='z'; next++) { word[i] = next; if (next == original || copyWorldList.find(word) == copyWorldList.end()) continue; neighbors.push_back(word); } word[i] = original; } return neighbors; } void BFS(string beginWord, string endWord, unordered_set<string> copyWorldList){ // remove "beginWorld" from copyWorldList if it is exist if(copyWorldList.find(beginWord) != copyWorldList.end()){ copyWorldList.erase(copyWorldList.find(beginWord)); } // BFS queue<string> q; q.push(beginWord); unordered_set<string> used; // store string that we have used used.insert(beginWord); while(!q.empty()){ vector<string> thisLayer; for(int i = q.size()-1; i>=0; i--){ string current = q.front(); q.pop(); vector<string> nextStep = findNext(current, copyWorldList); // find next step for(auto NEXT : nextStep){ thisLayer.push_back(NEXT); possibleSequence[current].push_back(NEXT); if(used.find(NEXT) == used.end()){ // if we have used, we don't need to push to queue q.push(NEXT); used.insert(NEXT); } } } // end this layer for(int i=0; i<thisLayer.size(); i++){ if(copyWorldList.find(thisLayer[i]) != copyWorldList.end()){ copyWorldList.erase(copyWorldList.find(thisLayer[i])); } } } } void DFS(string current, string target, vector<string> Path){ if(current == target) ans.push_back(Path); for(int i=0; i<possibleSequence[current].size(); i++){ Path.push_back(possibleSequence[current][i]); DFS(possibleSequence[current][i], target, Path); Path.pop_back(); } } vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) { unordered_set<string> copyWorldList(wordList.begin(), wordList.end()); if(copyWorldList.find(endWord) == copyWorldList.end()) return ans; BFS(beginWord, endWord, copyWorldList); vector<string> Path = {beginWord}; DFS(beginWord, endWord, Path); return ans; } }; ```