Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that: 1) Only one letter can be changed at a time, 2) Each intermediate word must exist in the dictionary.
For example, given: start = “hit”, end = “cog”, and dict = [“hot”,”dot”,”dog”,”lot”,”log”], return:
[ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
Java Solution
This is an extension of Word Ladder. The idea is the same. To track the actual ladder, we need to add a pointer that points to the previous node in the WordNode class. In addition, the used word can not directly removed from the dictionary. The used word is only removed when steps change.
class Solution { public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) { List<List<String>> result = new ArrayList<List<String>>(); HashSet<String> unvisited = new HashSet<>(); unvisited.addAll(wordList); LinkedList<Node> queue = new LinkedList<>(); Node node = new Node(beginWord,0,null); queue.offer(node); int minLen = Integer.MAX_VALUE; while(!queue.isEmpty()){ Node top = queue.poll(); //top if have shorter result already if(result.size()>0 && top.depth>minLen){ return result; } for(int i=0; i<top.word.length(); i++){ char c = top.word.charAt(i); char[] arr = top.word.toCharArray(); for(char j='z'; j>='a'; j--){ if(j==c){ continue; } arr[i]=j; String t = new String(arr); if(t.equals(endWord)){ //add to result List<String> aResult = new ArrayList<>(); aResult.add(endWord); Node p = top; while(p!=null){ aResult.add(p.word); p = p.prev; } Collections.reverse(aResult); result.add(aResult); //stop if get shorter result if(top.depth<=minLen){ minLen=top.depth; }else{ return result; } } if(unvisited.contains(t)){ Node n=new Node(t,top.depth+1,top); queue.offer(n); unvisited.remove(t); } } } } return result; } } class Node{ public String word; public int depth; public Node prev; public Node(String word, int depth, Node prev){ this.word=word; this.depth=depth; this.prev=prev; } } |
Wow. Wrong solution. Must be careful.
@disqus_ge9RooCt3l:disqus You should try yourself rather than googling for solution, Use your mind
What is the point of a solution which is getting failed for so many inputs?
Also failing for:
“red”
“tax”
[“ted”,”tex”,”red”,”tax”,”tad”,”den”,”rex”,”pee”]
Output:
[[“red”,”ted”,”tad”,”tax”],[“red”,”ted”,”tex”,”tax”]]
Expected:
[[“red”,”ted”,”tad”,”tax”],[“red”,”ted”,”tex”,”tax”],[“red”,”rex”,”tex”,”tax”]]
Fails for the input:
“hit”
“cog”
[“hot”,”dot”,”dog”,”lot”,”log”]
Expected Output is: []
True, solution is updated.
This code will remove all the previous steps words.
if(preNumSteps < currNumSteps){
unvisited.removeAll(visited);
}
We need to keep that word in the dict. We can’t remove that word as soon as we used it for the first time. Consider this case [“hit”,”hot”,”dot”,”dog”,”cog”], [“hit”,”hot”,”lot”,”log”,”cog”]. “hot” will come twice. If we will remove this we will unable to fetch the second case. In the previous question we only need length of sequence not the all the possible sequences of the shortest length. That’s why the author of this code is not deleting the repeated words as soon as it occurred.
I think this program will continue to search until all the words in the dict are removed, which is a huge performance issue. You need to terminate the search after you found all the shortest paths
Ok. I got the mistake I was making. Instead of doing top.numstep+1, I was doing ++top.numstep which was increasing step for even same step nodes and thus deleting each visited node on comparing prevstep < currstep.
Yes. Even I am getting only one path. Queue is becoming empty between the final loop.
the MinStep & top steps including If conditions are not necessary. I have removed them & all test cases passed.
———————————————————————————————————————-
public static List<List> wordLadderII(String start, String end, List wordList) {
Set dict = new HashSet(wordList);
List<List> paths = new ArrayList();
if (!dict.contains(end)) return paths; // if end not in dict then return
LinkedList queue = new LinkedList();
queue.add(new WNode(start, 1, null));// start que with start word & dist as 1
HashSet visited = new HashSet();
HashSet unvisited = new HashSet();
unvisited.addAll(dict);
int preDist = 0;
while (!queue.isEmpty()) {
WNode trav = queue.remove();
String word = trav.word;
int currDist = trav.dist;
// type this after you have typed the word forming logic
if (word.equals(end)) { // we ahve found
ArrayList list = new ArrayList();
list.add(trav.word);
while (trav.prev != null) {
list.add(0, trav.prev.word);
trav = trav.prev;
}
paths.add(list);
continue;
}
if (preDist < currDist) { // means we have reached here with the min dist thus there is no point in processsing these words again
unvisited.removeAll(visited);
}
preDist = currDist;
// new word forming logic
char[] arr = word.toCharArray();
for (int i = 0; i < arr.length; i++) {
for (char c = 'a'; c <= 'z'; c++) {
char temp = arr[i];
if (arr[i] != c) {
arr[i] = c;
}
String formed = new String(arr);
if (unvisited.contains(formed)) {
queue.add(new WNode(formed, trav.dist + 1, trav));
visited.add(formed);
}
arr[i] = temp;
}
}
}
return paths;
}
———————————————————————————————————————-
import java.util.*;
public class HelloWorld{
static ArrayList Dict = new ArrayList();
static String start = "hit";
static String end = "cog";
static ArrayList<ArrayList> ans = new ArrayList();
public static void main(String []args){
ArrayList ch = new ArrayList();
Dict.add("hot");
Dict.add("dot");
Dict.add("dog");
Dict.add("lot");
Dict.add("log");
findS(start, end, ch);
System.out.println(ans);
}
public static void findS(String s, String e, ArrayList ch){
char[] sToC = s.toCharArray();
ch.add(s);
for(int i=0;i<sToC.length;i++){
char old=sToC[i];
for(char c='a'; c<'z';c++){
sToC[i]=c;
if((String.valueOf(sToC)).equals(end)){
ch.add(String.valueOf(sToC));
System.out.println("Done");
ans.add(ch);
return;
}
if(Dict.contains(String.valueOf(sToC))){
if(!ch.contains(String.valueOf(sToC))){
ArrayList newA = copy(ch);
System.out.println("Gonna call recurse with " + String.valueOf(sToC));
findS(String.valueOf(sToC),end,newA);
}
}
}
sToC[i]=old;
}
}
public static ArrayList copy(ArrayList ch){
ArrayList newA = new ArrayList();
for(int i=0;i<ch.size();i++){
newA.add(ch.get(i));
}
return newA;
}
}
“BSF” Really :O
I don’t understand why would you need to keep the words in the dictionary (instead of deleting them right away, like in the previous case). If you reach a word again, it means that the result will be necessarily of a longer distance, so it can’t be in the shortest path. For the case of the end word, a simple validation when removing the word from the dict, will do the trick (or a different approach than having it in the dict).
I believe you should keep processing to get all the shortest path solutions, but filtering should be done at the WordNode level. That is: once you find the shortest distance, you don’t process nodes with greater distance than that. Also, because of the way the “graph” is generated in this case, you know that nodes are queued in increasing distance order, so you can stop processing once the best distance is passed.
No, You are thinking it in with 1 example only .
Code is correct on single Iteration of nested loop it will create all possible Threads for LinkedList.
Each thread will return the different path once word match is found.
Array List will hold all possible path.
you must remove used elements to avoid loops
Why remove used elements from hashset can boost performance? Isn’t that hashset is O(1) time complexity?
If anyone interested for both BFS and DFS approach for finding Word Ladder:
http://javabypatel.blogspot.in/2015/10/word-ladder-doublets-word-links-word-golf.html
I guess I am wrong. With BSF, the shortest path should be found first. Therefore, the first path should be the shorted path. The code is correct.
The code might miss some cases when there are multiple path from start to end with multiple length.
This seems to be wrong. It will only print one path. After that queue will be empty and loop wont run.
Am I inferring correctly?