Word Ladder Leetcode
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWordto endWord, such that:
- Only one letter can be changed at a time.
- Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]
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.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same.
UPDATE (2017/1/20):
The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.
public class Solution { int count = Integer.MAX_VALUE; Set<String> used; public int ladderLength(String beginWord, String endWord, List<String> wordList) { if (wordList == null || wordList.size() == 0) { return count; } used = new HashSet<>(); helper(beginWord, endWord, wordList, 1); return count == Integer.MAX_VALUE ? 0 : count; } private void helper(String beginWord, String endWord, List<String> wordList, int c) { if (beginWord.equals(endWord)) { count = Math.min(count, c); } for (String word : wordList) { if (used.contains(word)) { continue; } if (trans(beginWord, word)) { used.add(word); helper(word, endWord, wordList, c + 1); used.remove(word); } } } private boolean trans(String a, String b) { int l = a.length(); for (int i = 0; i < a.length(); i++) { if (a.charAt(i) != b.charAt(i)) { l--; } } return l == a.length() - 1; } }
bfs:
public class Solution { public int ladderLength(String beginWord, String endWord, List<String> wordList) { if (wordList == null || wordList.size() == 0) { return 0; } Set<String> used = new HashSet<>(); Queue<String> q = new LinkedList<>(); q.offer(beginWord); Set<String> hs = new HashSet<>(); hs.add(beginWord); return helper(endWord, wordList, q, hs); } private int helper(String endWord, List<String> wordList,Queue<String> q, Set<String> hs) { int count = 0; while (!q.isEmpty()) { int size = q.size(); for (int i = 0; i < size; i++) { String s = q.poll(); if (s.equals(endWord)) { return count + 1; } for (String word : wordList) { if (hs.contains(word)) { continue; } if (trans(s, word)) { q.offer(word); hs.add(word); } } } count++; } return 0; } private boolean trans(String a, String b) { int l = a.length(); for (int i = 0; i < a.length(); i++) { if (a.charAt(i) != b.charAt(i)) { l--; } } return l == a.length() - 1; } }
把判断重构词的方法改变了之后用bfs终于可以通过了。。。
public class Solution { public int ladderLength(String beginWord, String endWord, List<String> wordList) { if (wordList == null || wordList.size() == 0) { return 0; } Set<String> words = new HashSet<>(wordList); Queue<String> q = new LinkedList<>(); q.offer(beginWord); Set<String> used = new HashSet<>(); used.add(beginWord); return helper(endWord, words, q, used); } private int helper(String endWord, Set<String> wordList, Queue<String> q, Set<String> hs) { int count = 0; while (!q.isEmpty()) { int size = q.size(); for (int i = 0; i < size; i++) { String s = q.poll(); if (s.equals(endWord)) { return count + 1; } for (int j = 0; j < s.length(); j++) { char[] chars = s.toCharArray(); for (char c = 'a'; c <= 'z'; c++) { chars[j] = c; String trans = new String(chars); if (wordList.contains(trans)) { if (hs.add(trans)) { q.offer(trans); } } } } } count++; } return 0; } }
用了双边bfs,快了很多,从134ms变成39ms。思想就是两边同时开始找,那边扩张的范围小就从那边继续开始,这样就会比从一边开始扩张找到终点要快很多。
public class Solution { public int ladderLength(String beginWord, String endWord, List<String> wordList) { if (wordList == null || wordList.size() == 0) { return 0; } Set<String> words = new HashSet<>(wordList); if (!words.contains(endWord)) { return 0; } Set<String> beginSet = new HashSet<>(); Set<String> endSet = new HashSet<>(); Set<String> used = new HashSet<>(); beginSet.add(beginWord); endSet.add(endWord); used.add(beginWord); used.add(endWord); return helper(beginSet, endSet, words, used); } private int helper(Set<String> beginSet, Set<String> endSet, Set<String> wordList, Set<String> used) { int count = 0; while (!beginSet.isEmpty()) { if (beginSet.size() > endSet.size()) { Set<String> tmp = beginSet; beginSet = endSet; endSet = tmp; } Set<String> nextLevel = new HashSet<>(); for (String s : beginSet) { char[] chars = s.toCharArray(); for (int j = 0; j < s.length(); j++) { for (char c = 'a'; c <= 'z'; c++) { char old = chars[j]; chars[j] = c; String trans = new String(chars); if (endSet.contains(trans)) { return count + 2; } if (wordList.contains(trans)) { if (used.add(trans)) { nextLevel.add(trans); } } chars[j] = old; } } } beginSet = nextLevel; count++; } return 0; } }
而且可以每次把char再还原,就不用新建啦。今天发现每次leetcode跑出来的时间是不一样的。。居然纠结了好久。。。
还有种算法是不用新建一个used的set,就每次从wordlist里面移除用过的词。号称运用了Dijkstra's algorithm算法,虽然我也不知道这个算法是啥。。。懒得查了。。。
public class Solution { public int ladderLength(String beginWord, String endWord, List<String> wordList) { if (wordList == null || wordList.size() == 0) { return 0; } Set<String> words = new HashSet<>(wordList); if (!words.contains(endWord)) { return 0; } Set<String> beginSet = new HashSet<>(); Set<String> endSet = new HashSet<>(); Set<String> used = new HashSet<>(); beginSet.add(beginWord); endSet.add(endWord); words.remove(beginWord); words.remove(endWord); return helper(beginSet, endSet, words); } private int helper(Set<String> beginSet, Set<String> endSet, Set<String> wordList) { int count = 0; while (!beginSet.isEmpty()) { if (beginSet.size() > endSet.size()) { Set<String> tmp = beginSet; beginSet = endSet; endSet = tmp; } Set<String> nextLevel = new HashSet<>(); for (String s : beginSet) { char[] chars = s.toCharArray(); for (int j = 0; j < s.length(); j++) { for (char c = 'a'; c <= 'z'; c++) { char old = chars[j]; chars[j] = c; String trans = new String(chars); if (endSet.contains(trans)) { return count + 2; } if (wordList.contains(trans)) { nextLevel.add(trans); wordList.remove(trans); } chars[j] = old; } } } beginSet = nextLevel; count++; } return 0; } }
这道题断断续续磕磕绊绊做了这么久。。。终于结束了。。。= =