126. Word Ladder II



Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
1. Only one letter can be changed at a time
2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
* Return an empty list 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:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: []

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.





// need to debug this 


Your input
"hit"
"cog"
["hot","dot","dog","lot","log","cog"]
Your answer
[]
Expected answer
[["hit","hot","lot","log","cog"],["hit","hot","dot","dog","cog"]]



// my code, not passed 
class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        // use bfs to find the shortest path and use map tpo store parent child relations 
        // use dfs to get all the paths
        Set<String> dict = new HashSet<>();
        for(String word : dict) dict.add(word);
        
        Map<String, List<String>> map = new HashMap<>();
        for(String word : dict) map.put(word, new ArrayList<>());
        
        Map<String, Integer> distance = new HashMap<>();
        
        List<List<String>> res = new ArrayList<>();
        List<String> path = new ArrayList<>();
        
        bfs(beginWord, endWord, map, distance, dict);
        path.add(endWord);
        dfs(res, path, beginWord, endWord, distance, map);
        return res;
    }
    
    
    private void bfs(String beginWord, String endWord, Map<String, List<String>> map, Map<String, Integer> distance, Set<String> dict){
        Queue<String> queue = new LinkedList<>();
        int level = 1;
        boolean found = false;
        queue.offer(beginWord);
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i = 0; i < size; i++){
                String curWord = queue.poll();
                List<String> neis = getNei(dict, curWord);
                // if it's the first time visiting this nei, then add it in distance map 
                // else , don't update, since this is not the shortest distance from this nei to beginWord 
                for(String nei : neis){
                    if(!distance.containsKey(nei)){
                        distance.put(nei, level);
                    }
                    map.get(nei).add(curWord);
                    if(nei.equals(endWord)) found = true;
                }
                
            }
            if(found) break;
            level++;
        }   
    }
    
    private List<String> getNei(Set<String> dict, String curWord){
        List<String> res = new ArrayList<>();
        char[] curArray = curWord.toCharArray();
        for(int i = 0; i < curArray.length; i++){
            char c = curArray[i];
            for(char ch = 'a'; ch <= 'z'; ch++){
                curArray[i] = ch;
                if(ch == c) continue;
                curArray[i] = c;
                String newWord = new String(curArray);
                if(dict.contains(newWord)) res.add(newWord);
            }
        }
        return res;
    }
    
    private void dfs(List<List<String>> res, List<String> path, String beginWord, String endWord, Map<String, Integer> distance, Map<String, List<String>> map){
        // base case 
        if(beginWord.equals(endWord)){
            Collections.reverse(path);
            res.add(path);
        }
        if(map.get(endWord) != null && map.get(endWord).size() != 0){
            for(String word : map.get(endWord)){
                // only add the word, whose distance is endWord's distance - 1
                if(distance.get(endWord) == distance.get(word) + 1){
                    path.add(word);
                    dfs(res, path, beginWord, word, distance, map);
                    // backtracking 
                    path.remove(path.size() - 1);
                }
            }
        }
    }
}



// other’s code, accepted 

class Solution {
    public List<List<String>> findLadders(String start, String end, List<String> wordList) {
        HashSet<String> dict = new HashSet<String>(wordList);
        List<List<String>> res = new ArrayList<>();
        Map<String, List<String>> nodeNeis = new HashMap<>();
        HashMap<String, Integer> distance = new HashMap<>();
        List<String> solution = new ArrayList<>();
        
        dict.add(start);
        bfs(start, end, dict, nodeNeis, distance);
        solution.add(start);
        dfs(start, end, dict, nodeNeis, distance, solution, res);
        return res;
    }
    private void bfs(String start, String end, Set<String> dict, Map<String, List<String>> nodeNeis, Map<String, Integer> distance){
        for(String str : dict) nodeNeis.put(str, new ArrayList<>());
        
        Queue<String> queue = new LinkedList<>();
        queue.offer(start);
        distance.put(start, 0);
        
        while(!queue.isEmpty()){
            int size = queue.size();
            boolean foundEnd = false;
            for(int i = 0; i < size; i++){
                String cur = queue.poll();
                int curDistance = distance.get(cur);
                List<String> neis = getNeis(cur, dict);
                
                for(String nei : neis){
                    nodeNeis.get(cur).add(nei);
                    if(!distance.containsKey(nei)){
                        distance.put(nei, curDistance + 1);
                        if(end.equals(nei)){
                            foundEnd = true;
                        }else{
                            queue.offer(nei);
                        }
                    }
                }
                if(foundEnd){
                    break;
                }
            }   
        }   
    }
    private List<String> getNeis(String node, Set<String> dict){
        List<String> res = new ArrayList<>();
        char[] curArray = node.toCharArray();
        for(int i = 0; i < curArray.length; i++){
            char c = curArray[i];
            for(char ch = 'a'; ch <= 'z'; ch++){
                if(ch == c) continue;
                curArray[i] = ch;
                String curWord = new String(curArray);
                if(dict.contains(curWord)) res.add(curWord);
            }
            curArray[i] = c;
        }
        return res;   
    }
    
    private void dfs(String cur, String end, Set<String> dict, Map<String, List<String>> nodeNeis, Map<String, Integer> distance, List<String> solution, List<List<String>> res){
        
        if(end.equals(cur)){
            res.add(new ArrayList<>(solution));
        }else{
            for(String next : nodeNeis.get(cur)){
                if(distance.get(next) == distance.get(cur) + 1){
                    solution.add(next);
                    dfs(next, end, dict, nodeNeis, distance, solution, res);
                    solution.remove(solution.size() - 1);
                }
            }
        } 
    }
}


















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

// Output:
// [
//   ["hit","hot","dot","dog","cog"],
//   ["hit","hot","lot","log","cog"]
// ]

// map :       cog      

//           dog, log 


// distance :