Word Ladder
Given two words (start and end), and a dictionary, find the length of shortest transformation sequence 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"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
做的好费劲。。。记一下思路逐渐变化的过程吧。
想法1:想用动态规划,grid[i][j]表示set中第i个元素要变成第j个元素最小需要多少步,用了3重循环(grid[i][j] = grid[i][k]+grid[k][j]这种),不光是这3重循环,每个三重循环之后要判别矩阵中是否有元素变化,若有变化就重复做这3重循环,直到矩阵元素不变,结果就毫无疑问的超时了。。。
想法2:广度/深度优先遍历,后来发现复杂度还不如上面的做法呢。。。
想法3:看了一些网上的题解,想到了用图做,这个问题可以抽象图的最短路径问题,只不过可以简化一下,因为每一步的权重都是1,所以可以按层序遍历,后遍历到的一定比先遍历到的长度要长,所以第一次到达汇点的长度也就是最短长度。这样做还是超时。。。后来发现不是层序遍历的问题,时间好多都消耗在构造图上面了。原本的做法是用一个二维矩阵记录dict中两两之间的距离是否为1,即两个端点是否邻接,这样很耗时,后来参考别人的做法,改成在遍历时判断邻接,假设当前节点表示字符串hit,将hit中每一位换成a~z中的字符,判断新字符串是否在dict中并且还没有被遍历到,因为hashset查找的时间复杂度为O(1)。找到hit的所有邻接点的复杂度为O(26*hit的长度),这个复杂度在字符串长度较小时复杂度较高。原来找出一个顶点的邻接点的复杂度为O(n*字符串的长度),在n较大时肯定不如前面的好。然后精简了一下代码,得出代码如下:
1 public int ladderLength(String start, String end, Set<String> dict) { 2 dict.add(end); 3 Queue<String> queue = new LinkedList<String>(); 4 Map<String, Integer> map = new HashMap<String, Integer>(); 5 boolean found = false; 6 queue.add(start); 7 map.put(start, 1); 8 while (!queue.isEmpty()) { 9 String now = queue.poll(); 10 int level = map.get(now); 11 if (now.equals(end)) { 12 return level; 13 } 14 dict.remove(now); 15 for (int j = 0; j < now.length(); j++) { 16 for (int i = 0; i < 26; i++) { 17 StringBuilder sb = new StringBuilder(now); 18 sb.setCharAt(j, (char) ('a' + i)); 19 if (dict.contains(sb.toString()) 20 && !map.containsKey(sb.toString())) { 21 queue.add(sb.toString()); 22 map.put(sb.toString(), level + 1); 23 } 24 } 25 } 26 } 27 return 0; 28 }
耗时1060ms
在得出这个代码之前,还经历了一个超时的版本,就是在层序遍历的时候,每次遍历一层,这样需要另一个queue来记录下一层的元素,代码如下:
1 public int ladderLength(String start, String end, Set<String> dict) { 2 dict.add(end); 3 Queue<String> queue = new LinkedList<String>(); 4 int level = 1; 5 boolean found = false; 6 queue.add(start); 7 while (!queue.isEmpty()) { 8 Queue<String> temp = new LinkedList<String>(); 9 while (!queue.isEmpty()) { 10 String now = queue.poll(); 11 if (now.equals(end)) { 12 return level; 13 } 14 dict.remove(now); 15 for (int j = 0; j < now.length(); j++) { 16 for (int i = 0; i < 26; i++) { 17 StringBuilder sb = new StringBuilder(now); 18 sb.setCharAt(j, (char) ('a' + i)); 19 if (dict.contains(sb.toString())) { 20 temp.add(sb.toString()); 21 } 22 } 23 } 24 } 25 level++; 26 queue = temp; 27 } 28 return 0; 29 }
超时的原因不是很清楚,是因为维护了另一个queue么????