【LeetCode】WordLadder||
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.
这道题目跟word ladder的区别在于要记录下来所遍历过的最短路径,我首先想到的用DFS算法来解决,DFS过程中记录下来路径,但是由于需要遍历到所有的路径所以时间
消耗比较大,我又设置了一个全局的遍历来记录最小的长度,当遍历取得最小长度后,在此后的遍历中如果大于最小长度则直接返回。但是由于存在一些之前多遍历的路径最后还是时间超时。
public class Solution { public ArrayList<ArrayList<String>> findLadders(String start, String end, HashSet<String> dict) { ArrayList<ArrayList<String>> ree = new ArrayList<ArrayList<String>>(); if(dict.isEmpty()) return re; if(start.equals(end)) return re; ArrayList<String> init = new ArrayList<String>(); init.add(start); int depth=0; DFS(start,end,dict,init,depth); Iterator<ArrayList<String>> it = re.iterator(); while(it.hasNext()){ ArrayList<String> temps = it.next(); if(temps.size()==mindepth+1) ree.add(temps); } return re; } public int mindepth=-1; public ArrayList<ArrayList<String>> re = new ArrayList<ArrayList<String>>(); private void DFS(String start, String end, HashSet<String> dict,ArrayList<String> init,int depth) { depth++; if(mindepth!=-1&&depth>mindepth) return; if(dict.isEmpty()) return; int len = start.length(); char[] sr = start.toCharArray(); for(int i=0;i<len;i++){ char temp = sr[i]; for(char c='a';c<='z';c++){ if(temp==c) continue; sr[i]=c; String ns = String.valueOf(sr); if(ns.equals(end)){ if(mindepth==-1){ mindepth=depth; }else{ if(depth<mindepth){ mindepth=depth; } } init.add(end); ArrayList<String> al = new ArrayList<String>(); int m=0; while(m<init.size()){ al.add(m, init.get(m)); m++; } re.add(al); init.remove(init.size()-1); } if(dict.contains(ns)){ init.add(ns); dict.remove(ns); DFS(ns,end,dict,init,depth); dict.add(ns); init.remove(init.size()-1); } } sr[i]=temp; } return; } }
后面还是想到用BFS算法,但是此问题的难点在于对路径的记录上面。
整个过程可以分成两个部分:先通过BFS从start找到end,在找的过程中需要记录前驱单词,再用DFS反向找回完整路径。
但是用Java实现上述过程会遇到TLE。为了能让用时尽可能短,有如下几点需要注意的地方:
1. 由于最后生成路径的时候,需要从end找到start构造ArrayList,即使用LinkList来协助构造,性能也不好。解决办法:不从start找end了,反过来从end找start,找到后,再从start往end构造路径,性能会有明显提升。
2. 在BFS过程中,需要替换String的每一位字符,先转换成char数组再操作,性能也会有明显提升。
3. 在BFS过程中,注意避免一些不必要的搜索
分析:本题主要的框架和上一题是一样,但是还要解决两个额外的问题:一、 怎样保证求得所有的最短路径;二、 怎样构造这些路径
第一问题:
- 不能像上一题那样,找到一个单词相邻的单词后就立马把它从字典里删除,因为当前层还有其他单词可能和该单词是相邻的,这也是一条最短路径,比如hot->hog->dog->dig和hot->dot->dog->dig,找到hog的相邻dog后不能立马删除,因为和hog同一层的单词dot的相邻也是dog,两者均是一条最短路径。但是为了避免进入死循环,再hog、dot这一层的单词便利完成后dog还是得从字典中删除。即等到当前层所有单词遍历完后,和他们相邻且在字典中的单词要从字典中删除。
- 为了解决上面的问题,每次层序遍历结束后将tempqueue赋值给queue,然后将temequeue清空,重新记录下层需要遍历的数据
- 当某一层的某个单词转换可以得到end单词时,表示已经找到一条最短路径,那么该单词的其他转换就可以跳过。并且遍历完这一层以后就可以跳出循环,因为再往下遍历,肯定会超过最短路径长度
public class NSolution { public ArrayList<ArrayList<String>> findLadders(String start, String end, HashSet<String> dict) { Map<String,ArrayList<String>> map = new HashMap<String,ArrayList<String>>(); ArrayList<ArrayList<String>> relist = new ArrayList<ArrayList<String>>(); if(dict.isEmpty()) return relist; if(start.equals(end)) return relist; Iterator<String> it = dict.iterator(); HashSet<String> tempdict = new HashSet<String>(); while(it.hasNext()){ String str = it.next(); tempdict.add(str); map.put(str, new ArrayList<String>()); } Queue<String> queue = new LinkedList<String>(); Queue<String> tempqueue = new LinkedList<String>(); queue.add(start); map.put(start,new ArrayList<String>()); boolean flag = true; while(dict!=null&&!queue.isEmpty()){ String str = queue.peek(); char[] sr = str.toCharArray(); queue.poll(); int len = str.length(); for(int i=0;i<len;i++){ char temp = sr[i]; for(char c ='a';c<='z';c++){ if(c==temp) continue; sr[i]=c; String ns = String.valueOf(sr); if(ns.equals(end)){ flag=false; ArrayList<String> artemp = new ArrayList<String>(); artemp= map.get(ns); if(ns.isEmpty()){ artemp.add(str); }else{ Iterator<String> ita = artemp.iterator(); boolean bol = false; while(ita.hasNext()){ if(ita.next().equals(str)){ bol = true;; } } if(!bol){ artemp.add(str); } } } if(dict.contains(ns)){ tempdict.remove(ns); if(!tempqueue.contains(ns)){ tempqueue.offer(ns); } ArrayList<String> artemp = new ArrayList<String>(); artemp= map.get(ns); if(ns.isEmpty()){ artemp.add(str); }else{ Iterator<String> ita = artemp.iterator(); boolean bol = false; while(ita.hasNext()){ if(ita.next().equals(str)){ bol = true;; } } if(!bol){ artemp.add(str); } } } } sr[i]=temp; } if(queue.isEmpty()&&flag){ queue=tempqueue; tempqueue= new LinkedList<String>(); dict =new HashSet<String>(); Iterator<String> iitt = tempdict.iterator(); while(iitt.hasNext()){ dict.add(iitt.next()); } } } ArrayList<String> tmparray = new ArrayList<String>(); tmparray.add(end); buildpath(start, end, map, tmparray, relist); return relist; } public void buildpath(String start, String end, Map<String, ArrayList<String>> map, ArrayList<String> tmparray, ArrayList<ArrayList<String>> res) { ArrayList<String> pre = map.get(end); if (end.equals(start)) { ArrayList<String> tmparray2 = new ArrayList<String>(tmparray); Collections.reverse(tmparray2); res.add(tmparray2); return; } for (String s: pre) { tmparray.add(s); buildpath(start, s, map, tmparray, res); tmparray.remove(tmparray.size() - 1); } } }