Word Ladder II

Given two words (start and end), and a dictionary, find the shortest transformation sequence from start to end, such that:

If there are multiple such sequence of shortest length, return all of them. Refer to the example for more details.

Note:

Input Format

The first argument is string start.
The second argument is string end.
The third argument is an array of strings dict

Output Format

Return all transformation sequences such that first word of each sequence is start and last word is end, all intermediate words belongs to dictionary(dict) and consecutive words had atmost 1 difference.  

Example :

:

start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

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

public class Solution {
    public ArrayList<ArrayList<String>> findLadders(String beginWord, String endWord, ArrayList<String> wordList) {
        ArrayList<ArrayList<String>> result = new ArrayList<>();
        Map<String, ArrayList<String>> map = new HashMap<>();
        Map<String, Integer> distance = new HashMap<>();
        Set<String> words = new HashSet<>(wordList);
        Deque<String> queue = new ArrayDeque<>();
        queue.offer(beginWord);
        map.put(beginWord, new ArrayList<>());
        distance.put(beginWord, 0);
        boolean found = false;

        // bfs
        /*
        aa: [ba, ab]
        bb: [ab, ba]
        ba: [aa, bb]
        ~~~~~~~~~~~~
        aa: 1
        bb: 1
        ab: 2
        ba: 0
        */
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i ++) {
                String curr = queue.poll();
                if (curr.equals(endWord)) {
                    found = true;
                }
                map.put(curr, new ArrayList<>());
                for (int j = 0; j < curr.length(); j ++) {
                    char[] arr = curr.toCharArray();
                    for (char k = 'a'; k <= 'z'; k ++) {
                        arr[j] = k;
                        String next = new String(arr);
                        if (!curr.equals(next) && words.contains(next)) {
                            map.get(curr).add(next);
                            if (!distance.containsKey(next)) {
                                distance.put(next, distance.get(curr) + 1);
                                queue.offer(next);
                            }
                        }
                    }
                }
            }
            if (found) break;
        }
        // for (String key: map.keySet()) {
        //     System.out.print(key + ": ");
        //     System.out.print(Arrays.toString(map.get(key).toArray()));
        //     System.out.println();
        // }
        // System.out.println("~~~~~~~~~~~~");

        // for (String key: distance.keySet()) {
        //     System.out.print(key + ": ");
        //     System.out.print(distance.get(key));
        //     System.out.println();
        // }

        backtracking(result, map, distance, beginWord, endWord, new ArrayList<>());
        return result;
    }

    private void backtracking(ArrayList<ArrayList<String>> result, Map<String, ArrayList<String>> map, Map<String, Integer> distance, String start, String end, ArrayList<String> curr) {
        if (start.equals(end)) {
            curr.add(end);
            result.add(new ArrayList<>(curr));
            curr.remove(curr.size() - 1);
            return;
        }
        // it's possible start was not visited yet
        if (map.containsKey(start)) {
            // System.out.println(start);
            curr.add(start);
            for (String next : map.get(start)) {
                if (distance.get(next) == distance.get(start) + 1) {
                    backtracking(result, map, distance, next, end, curr);
                }
            }
            curr.remove(curr.size() - 1);
        }
    }
}