Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
Note:
public class Solution { public int ladderLength(String start, String end, Set<String> dict) {//字典中可能含有start和end //dict.add(start); //dict.add(end); String[] arr = (String[])dict.toArray(new String[0]); //将dict变成数组,方便记录每一个字典项转换成其他字典项需要的转换次数 int[][] c = new int[arr.length][arr.length];//相当于邻接矩阵 for(int i = 0 ; i < arr.length ; i ++){//构建图的邻接矩阵 for(int j = i + 1 ; j < arr.length ; j ++){ c[i][j] = count(arr[i],arr[j]); c[j][i] = c[i][j];//对称 } } Set<Integer> set = new HashSet<Integer>();//存储到end的转换次数为1的节点 for(int i = 0; i < arr.length ; i ++){ if(count(arr[i],end) == 1 || end.equals(arr[i])){//相等的也要纳入其中 set.add(i); } } //利用邻接矩阵找出两个点之间的最短通路 int len = 0; int level = 0; Queue<Integer> queue = new LinkedList<Integer>(); int[] visited = new int[arr.length]; for(int i = 0 ; i < arr.length ; i ++){ if(count(start,arr[i]) == 1 || start.equals(arr[i])){ queue.offer(i); visited[i] = 1; level++; } } //visited[startIndex] = 1; while(queue.size() > 0){ int count = 0; int nextLevel = 0;//下一层的元素数 len ++; while(count < level){//第len层全部出队 int tmp = queue.poll(); if(set.contains(tmp)){ return len + 2; } for(int i = 0 ; i < arr.length ; i ++){ if(visited[i] != 1 && c[tmp][i] == 1){ queue.offer(i); visited[i] = 1; nextLevel++; } } count ++; } level = nextLevel; } return 0; } public int count(String s1, String s2){//字符串之间是否是一次交换 int count = 0; for(int i = 0 ; i < s1.length(); i++){ if(s1.charAt(i) != s2.charAt(i)){ count++; if(count > 1){ break; } } } if(count == 1){//可以直达, return 1; }else{//不可直达 return 0; } } }
继续度娘...........
有大神提出了另一种方案,不需要构造邻接矩阵,思路就是将字符串中的每一个位置用‘a’~‘z’都都换一次,然后在查看这个字符串是否在字典里,这样就等于找到了所有字典中与原字符串相差为1的字符串。然后就是利用BFS的思想搜索。
public class Solution { public int ladderLength(String start, String end, HashSet<String> dict) { if (start == null || end == null || start.equals(end)) return 0; if (isOneWordDiff(start, end)) return 2; Queue<String> queue=new LinkedList<String>();//访问队列 Set<String> visited = new HashSet<String>();//表示字典里的对应key访问过了 int level = 0;//某一层的元素数 int len = 0; //将能够进行一次转换就到字典里的string加到队列,也就是离start最近的 StringBuilder sb = new StringBuilder(start);//重复修改字符串并且是单线程使用StringBuilder for(int i = 0 ; i < start.length(); i++){ char tmp = sb.charAt(i); for(char j = 'a' ; j <= 'z' ; j ++){ if(j == tmp){ continue; }else{ sb.setCharAt(i,j); if(dict.contains(sb.toString())){ queue.offer(sb.toString()); level ++; visited.add(sb.toString()); } } } sb.setCharAt(i,tmp); } while(!queue.isEmpty()) { len++; int count = 0; int nextLevel = 0; while(count < level){//控制出队的个数 String tmp = queue.poll(); if(isOneWordDiff(tmp,end)){ return len + 2; }else{ sb = new StringBuilder(tmp); for(int i = 0 ; i < tmp.length(); i++){ char c = sb.charAt(i); for(char j = 'a' ; j <= 'z' ; j ++){ if(j == c){ continue; }else{ sb.setCharAt(i,j); if(dict.contains(sb.toString()) && !visited.contains(sb.toString())){ queue.offer(sb.toString()); nextLevel ++; visited.add(sb.toString()); } } } sb.setCharAt(i,c); } } count++; } level = nextLevel; } return 0; } private boolean isOneWordDiff(String a, String b) { int diff = 0; for (int i = 0; i < a.length(); i++) { if (a.charAt(i) != b.charAt(i)) { diff++; if (diff >= 2) break; } } return diff == 1; } }
继续精简代码,在上一个算法,并没有将start和end加入字典,而且字典中有跟start和end相同的字符串出现,而字典中的这些start和end是没有作用的,白白浪费了26*length*2的时间。这次将start和end都加入字典中。
public class Solution { public int ladderLength(String start, String end, HashSet<String> dict) { if (start == null || end == null) return 0; if (isOneWordDiff(start, end)) return 2; dict.add(start);//即使字典已经包含start和end也无所谓 dict.add(end); Queue<String> queue=new LinkedList<String>();//访问队列 Set<String> visited = new HashSet<String>();//表示字典里的对应key访问过了 StringBuilder sb = new StringBuilder(start);//重复修改字符串并且是单线程使用StringBuilder int level = 0;//某一层的元素数 int len = 0; queue.offer(start); level++; visited.add(start); while(!queue.isEmpty()) { len++; int count = 0; int nextLevel = 0; while(count < level){//控制出队的个数 String tmp = queue.poll(); if(tmp.equals(end)){ return len; }else{ //将能够进行一次转换就到字典里的string加到队列,也就是离tmp最近的 sb = new StringBuilder(tmp);//重复修改字符串并且是单线程使用StringBuilder for(int i = 0 ; i < tmp.length(); i++){ char c = sb.charAt(i); for(char j = 'a' ; j <= 'z' ; j ++){ if(j == c){ continue; }else{ sb.setCharAt(i,j); if(dict.contains(sb.toString()) && !visited.contains(sb.toString())){ queue.offer(sb.toString()); nextLevel ++; visited.add(sb.toString()); } } } sb.setCharAt(i,c); } } count++; } level = nextLevel; } return 0; } private boolean isOneWordDiff(String a, String b) { int diff = 0; for (int i = 0; i < a.length(); i++) { if (a.charAt(i) != b.charAt(i)) { diff++; if (diff >= 2) break; } } return diff == 1; } }
Runtime: 662 ms
终于完成了,用了一天的时间,想法花费的时间还行,就是实现起来,各种问题接踵而来,最无奈的就是跟字符串打交道,太麻烦了