package com.interview.graph; import java.util.*; /** * Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that: * Only one letter can be changed at a time * Each intermediate word must exist in the word list * * Solution - * Since we have to find all paths we need care about below extra stuff on top of regular BFS * 1) Maintain parent of every word to recreate the path * 2) Maintain 2 visited. First is for level and once the entire level is done then move it to visited. * 3) Also since we are looking result from beginWord to endWord switch them initially and go from endWord towards beginWord. * * https://leetcode.com/problems/word-ladder-ii/ */ public class WordLadder { public List<List<String>> findLadders(String beginWord, String endWord, Set<String> wordList) { if (wordList == null || wordList.size() == 0) { return Collections.EMPTY_LIST; } String temp = endWord; endWord = beginWord; beginWord = temp; Map<String, List<String>> parent = new HashMap<>(); Queue<String> queue1 = new LinkedList<>(); Set<String> visited = new HashSet<>(); Set<String> levelVisited = new HashSet<>(); List<List<String>> result = new ArrayList<>(); parent.put(beginWord, null); queue1.offer(beginWord); visited.add(beginWord); boolean foundDestination = false; while (!queue1.isEmpty()) { while (!queue1.isEmpty()) { String word = queue1.poll(); for (int i = 0; i < word.length(); i++) { char wordArray[] = word.toCharArray(); for (char ch = 'a'; ch <= 'z'; ch++) { wordArray[i] = ch; String newWord = new String(wordArray); if (!endWord.equals(newWord) && (!wordList.contains(newWord) || visited.contains(newWord))) { continue; } List<String> parents = parent.get(newWord); if (parents == null) { parents = new ArrayList<>(); parent.put(newWord, parents); } parents.add(word); levelVisited.add(newWord); if (endWord.equals(newWord)) { foundDestination = true; break; } } } } if (foundDestination) { break; } for (String word : levelVisited) { queue1.offer(word); visited.add(word); } levelVisited.clear(); } if (!foundDestination) { return Collections.EMPTY_LIST; } else { setParent(parent, beginWord, new ArrayList<>(), endWord, result); } return result; } private void setParent(Map<String, List<String>> parent, String startWord, List<String> path, String currentWord, List<List<String>> result) { path.add(currentWord); if (startWord.equals(currentWord)) { result.add(new ArrayList<>(path)); path.remove(path.size() - 1); return; } for (String p : parent.get(currentWord)) { setParent(parent, startWord, path, p, result); } path.remove(path.size() - 1); } public static void main(String args[]) { String[] wordList = {"hot","dot","dog","lot","log"}; Set<String> wordSet = new HashSet<>(); wordSet.addAll(Arrays.asList(wordList)); WordLadder wl = new WordLadder(); List<List<String>> result = wl.findLadders("hit", "cog", wordSet); System.out.print(result); } }