import java.util.*; /** * Given two words (start and end), and a dictionary, find all shortest * transformation sequence(s) from start to end, such that: * * Only one letter can be changed at a time * Each intermediate word must exist in the dictionary * For example, * * Given: * start = "hit" * end = "cog" * dict = ["hot","dot","dog","lot","log"] * Return * [ * ["hit","hot","dot","dog","cog"], * ["hit","hot","lot","log","cog"] * ] * Note: * All words have the same length. * All words contain only lowercase alphabetic characters. * * Tags: Array, Backtracking, BFS, String */ class WordLadder2 { public static void main(String[] args) { String start = "hit"; String end = "cog"; String[] arr = {"hot","dot","dog","lot","log"}; Set<String> dict = new HashSet<String>(Arrays.asList(arr)); System.out.println(new WordLadder2().findLadders(start, end, dict).toString()); } /** * BFS then DFS */ public List<List<String>> findLadders(String start, String end, Set<String> dict) { List<List<String>> res = new ArrayList<List<String>>(); Map<String, List<String>> map = new HashMap<String, List<String>>(); Map<String, Integer> dist = new HashMap<String, Integer>(); bfs(map, dist, start, end, dict); dfs(res, new LinkedList<String>(), end, start, dist, map); return res; } /** * Create a queue, add start to it and put start in dist map * Initialize map with lists */ void bfs(Map<String, List<String>> map, Map<String, Integer> dist, String start, String end, Set<String> dict) { Queue<String> q = new LinkedList<String>(); q.offer(start); dict.add(start); // make sure start and end in dictionary dict.add(end); dist.put(start, 0); for (String s : dict) map.put(s, new ArrayList<String>()); while (!q.isEmpty()) { String word = q.poll(); List<String> expansion = expand(word, dict); // generate all words for (String next : expansion) { map.get(next).add(word); if (!dist.containsKey(next)) { // not in dist map yet dist.put(next, dist.get(word) + 1); q.offer(next); } } } } /** * Generate a list of words the word * Skip if it's the same character * If word is in dictionary, add to expansion list */ List<String> expand(String word, Set<String> dict) { List<String> res = new ArrayList<String>(); for (int i = 0; i < word.length(); i++) { for (char ch = 'a'; ch <= 'z'; ch++) { char[] chs = word.toCharArray(); if (ch != chs[i]) { chs[i] = ch; String next = new String(chs); if (dict.contains(next)) res.add(next); } } } return res; } /** * addRecursive current word to first position * addRecursive path to result if word is start */ void dfs(List<List<String>> res, List<String> path, String word, String start, Map<String, Integer> dist, Map<String, List<String>> map) { if (word.equals(start)) { path.add(0, word); res.add(new ArrayList<String>(path)); path.remove(0); return; // note to return } for (String next : map.get(word)) { if (dist.containsKey(next) && dist.get(word) == dist.get(next) + 1) { // backward, so word = next + 1 path.add(0, word); // add current word dfs(res, path, next, start, dist, map); // dfs next word path.remove(0); } } } }