[LeetCode] Word Ladder II
2014-03-20 13:58
288 查看
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 =
end =
dict =
Return
Note:
All words have the same length.
All words contain only lowercase alphabetic characters.
这题做得真累,尝试了几种不同的解法,感觉一不小心就会超时了。
这题很明显和上一题一样,仍然应该使用BFS,关键是要保存所有的最短路径而非单个最短距离。如何保存呢?这里我使用了两种不同的思路。
思路1:自定义一个Node类,里面保存当前节点的所有前驱,然后再用DFS去从end到start递推获得所有路径。
思路2:使用Set<List<String>>。虽然结果需要的形式为List<List<String>>,可以先用Set保存各个路径来达到去重的目的,然后调用容器类的addAll方法即可。
和上一题比较不同的是,当前str为一个字典词,且也在map里时,需要进一步判断:如果这条路径的长度和当前所保存的路径长度相等,那么也应该把当前路径加入到路径集合中。但是这里不能把这个str再次加入到搜索队列,因为这个str已经出现在map中了,说明之前肯定已经搜索到了,在第一次搜索到的时候已经加入到了搜索队列中。如果再次加入搜索队列,则会导致重复搜索而超时。
思路1实现:
思路2实现:
思路2由于不需要使用DFS去还原重构所求路径,也不需要自定义类,所以代码会稍微简短点。但是频繁的对中间结果路径的拷贝导致效率相对偏低,思路一会更快一点。
另外有一段代码是两个思路都需要的实现的一个子函数,用于还原路径。
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.
这题做得真累,尝试了几种不同的解法,感觉一不小心就会超时了。
这题很明显和上一题一样,仍然应该使用BFS,关键是要保存所有的最短路径而非单个最短距离。如何保存呢?这里我使用了两种不同的思路。
思路1:自定义一个Node类,里面保存当前节点的所有前驱,然后再用DFS去从end到start递推获得所有路径。
思路2:使用Set<List<String>>。虽然结果需要的形式为List<List<String>>,可以先用Set保存各个路径来达到去重的目的,然后调用容器类的addAll方法即可。
和上一题比较不同的是,当前str为一个字典词,且也在map里时,需要进一步判断:如果这条路径的长度和当前所保存的路径长度相等,那么也应该把当前路径加入到路径集合中。但是这里不能把这个str再次加入到搜索队列,因为这个str已经出现在map中了,说明之前肯定已经搜索到了,在第一次搜索到的时候已经加入到了搜索队列中。如果再次加入搜索队列,则会导致重复搜索而超时。
思路1实现:
public class Node { public int dist; public String str; public LinkedList<Node> prev; public Node(int dist, String str) { this.dist = dist; this.str = str; this.prev = new LinkedList<Node>(); } public void addPrev(Node pNode) { prev.add(pNode); } } public List<List<String>> findLadders(String start, String end, Set<String> dict) { dict.add(end); // Key: the dictionary string; Value: Node. Map<String, Node> map = new HashMap<String, Node>(); Queue<String> queue = new LinkedList<String>(); Node startNode = new Node(1, start); queue.offer(start); map.put(start, startNode); List<List<String>> ret = new ArrayList<List<String>>(); while (!queue.isEmpty()) { String str = queue.poll(); if (str.equals(end)) { getPaths(map.get(end), map, new ArrayList<String>(), ret); return ret; } for (int i = 0; i < str.length(); i++) { for (int j = 0; j < 26; j++) { char c = (char) ('a' + j); String newStr = replace(str, i, c); // If a new word is explored. if (dict.contains(newStr)) { if (!map.containsKey(newStr)) { // Construct a new node. Node node = map.get(str); Node newNode = new Node(node.dist + 1, newStr); newNode.prev = new LinkedList<Node>(); newNode.prev.add(node); map.put(newStr, newNode); queue.offer(newStr); } else { Node node = map.get(newStr); Node prevNode = map.get(str); // Increase the path set. if (node.dist == prevNode.dist + 1) { node.addPrev(prevNode); // queue.offer(newStr); // This will cause TLE. } } } } } } return ret; // Return an empty set. }
思路2实现:
public List<List<String>> findLadders(String start, String end, Set<String> dict) { dict.add(end); // Key: the dictionary string; Value: Set<List<String>>. Map<String, Set<List<String>>> map = new HashMap<String, Set<List<String>>>(); Queue<String> queue = new LinkedList<String>(); List<String> startPath = new ArrayList<String>(); startPath.add(start); Set<List<String>> startSet = new HashSet<List<String>>(); startSet.add(startPath); queue.offer(start); map.put(start, startSet); List<List<String>> ret = new ArrayList<List<String>>(); while (!queue.isEmpty()) { String str = queue.poll(); if (str.equals(end)) { ret.addAll(map.get(end)); return ret; } for (int i = 0; i < str.length(); i++) { for (int j = 0; j < 26; j++) { // Transform it into another word. String newStr = replace(str, i, (char) ('a' + j)); // If a new word is explored. if (dict.contains(newStr)) { if (!map.containsKey(newStr)) { // Construct a new path set. Set<List<String>> prevSet = map.get(str); Set<List<String>> newSet = new HashSet<List<String>>(); for (List<String> path : prevSet) { List<String> newPath = new ArrayList<String>( path); newPath.add(newStr); newSet.add(newPath); } map.put(newStr, newSet); queue.offer(newStr); } else { Set<List<String>> prevSet = map.get(str); Set<List<String>> newSet = map.get(newStr); Iterator<List<String>> prevIt = prevSet.iterator(); Iterator<List<String>> newIt = newSet.iterator(); // Increase the path set. if (prevIt.next().size() + 1 == newIt.next().size()) { for (List<String> path : prevSet) { List<String> newPath = new ArrayList<String>( path); newPath.add(newStr); newSet.add(newPath); // queue.offer(newStr); // This will cause TLE. } } } } } } } return ret; // Return an empty set. } // Replace a character at the given index of str, with c. private String replace(String str, int index, char c) { StringBuilder sb = new StringBuilder(str); sb.setCharAt(index, c); return sb.toString(); }
思路2由于不需要使用DFS去还原重构所求路径,也不需要自定义类,所以代码会稍微简短点。但是频繁的对中间结果路径的拷贝导致效率相对偏低,思路一会更快一点。
另外有一段代码是两个思路都需要的实现的一个子函数,用于还原路径。
// Get all the paths by using DFS. private void getPaths(Node end, Map<String, Node> map, List<String> curPath, List<List<String>> paths) { if (end == null) { paths.add(curPath); return; } curPath.add(0, end.str); if (!end.prev.isEmpty()) { for (Node prevNode : end.prev) { getPaths(prevNode, map, new ArrayList<String>(curPath), paths); } } else { getPaths(null, map, curPath, paths); } }
相关文章推荐
- Palindrome Number
- Max Points on a Line
- Evaluate Reverse Polish Notation
- Sort List
- Insertion Sort List
- LRU Cache
- Binary Tree Postorder Traversal
- Binary Tree Preorder Traversal
- Reorder List
- Linked List Cycle
- Linked List Cycle II
- Word Break
- Word Break II
- Copy List with Random Pointer
- Single Number
- Single Number II
- Candy
- Gas Station
- Clone Graph
- Palindrome Partitioning