Given two words ( start and end ), and a dictionary, find all 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
Note:
- All words have the same length.
- All words contain only lowercase alphabetic characters.
尝试用Trie 来实现这个。
最后TLE(out of time),不过算法已经正确了。
使用Trie,扩展性更好。 可以时时的加减dict里面的word, 也可以被多次调用。
1 package leetcode; 2 3 import java.util.ArrayList; 4 import java.util.HashSet; 5 import java.util.Iterator; 6 import java.util.List; 7 import java.util.Set; 8 9 // implement wordLadder with Trie. 10 /* 11 * 如果这个函数要多次被调用,怎么办? 12 * 因为我每次都用挨个修改字母的办法来寻找新word,但如果能先对dictionary做个预处理,弄个从word到set of words之间的mapping,那么每轮BFS就可以省去寻找新词的时间。预处理字典的复杂度也是O(size of dictionary) * (length of string) * 26。最终可以用一个Hash<String, Set<String>>来表示这个mapping关系。 13 * 14 * 如果这个字典要增加/移除单词怎么办? 15 * 增加的话,只需要对新单词做同样的事情:修改每个字母找下一个单词,然后那个被找到下一个单词的mapping里同时也添加进新单词。因为这个关系是可互换的,如果wordA => wordB, 那么wordB => wordA。 16 * 删除的时候同理,对于map.get(toBeRemoved)里的所有单词的mapping集合都删掉这个toBeRemoved,然后再从map里面去掉它自己,写出来的代码大概是这样: 17 * for (String s : map.get(toBeRemoved)) map.get(s).remove(toBeRemoved); 18 * map.remove(toBeRemoved); 19 * 这个地方我一开始没注意到可以互换,以为还得预处理一个反方向的mapping,经过面试官两次提醒才发现了这个规律…… 20 * 21 * 如果这个字典变得很庞大,这个mapping和字典本身就太占空间了,怎么处理? 22 * 用trie记录所有单词,每个node用一个bit记录这个node是否为一个单词,并且有一个set<myTrieNode>的field记录他可以变成的其他单词。至于如何构建这个field,利用trie的特性,可以用回溯的方法非常简单地找到可变过去的单词(在碰到公共ancestor之前,路径上的node个数必须相同,这个用于保证长度,并且至多只有一个字母不同,用于满足mapping的条件) 23 * 至此……时间终于用完了,问了下他的team的情况,就结束了…… 24 * 25 * */ 26 27 class myTrieNode{ 28 Boolean isWord; 29 myTrieNode[] childList; 30 Integer freq; 31 char val; 32 String word; 33 34 public myTrieNode(){ // use to generate root of Trie 35 this .freq = 0 ; 36 this .childList = new myTrieNode[26 ]; 37 this .isWord = false ; 38 } 39 40 public myTrieNode( char c){ 41 this .val = c; 42 this .freq = 1 ; 43 this .childList = new myTrieNode[26 ]; 44 } 45 46 public String containsWord(String s){ 47 if (s == null || s.length() == 0 ){ 48 if ( this .word != null && this .isWord == true ) return this .word; 49 else return "" ; 50 } 51 int len = s.length(); 52 myTrieNode cur = this ; 53 for ( int i = 0; i < len; i ++ ){ 54 char c = s.charAt(i); 55 if (cur.childList[c - 'a'] != null ){ 56 cur = cur.childList[c - 'a' ]; 57 } else { 58 return null ; 59 } 60 } 61 return cur.isWord == true ? cur.word : null ; 62 } 63 } 64 class myTrie{ 65 myTrieNode root; 66 67 public myTrie(){ 68 this .root = new myTrieNode(); 69 } 70 71 public void insert(String word){ 72 if (word == null || word.length() == 0) return ; 73 int len = word.length(); 74 myTrieNode cur = this .root; 75 for ( int i = 0; i < len; i ++ ){ 76 char c = word.charAt(i); 77 if (cur.childList[c - 'a'] == null ){ 78 cur.childList[c - 'a'] = new myTrieNode(c); 79 } else { 80 cur.childList[c - 'a'].freq ++ ; 81 } 82 cur = cur.childList[c - 'a' ]; 83 if (i == word.length() - 1 ){ 84 cur.isWord = true ; 85 cur.word = word; 86 } 87 } 88 } 89 90 } 91 public class WordLadder { 92 93 94 private static myTrie trie; 95 private static List<List<String>> result; 96 public static List<List<String>> findLadders(String start, String end, Set<String> dict) { 97 trie = generateTrie(dict); 98 trie.insert(end); 99 result = new ArrayList<List<String>> (); 100 ArrayList<String> row = new ArrayList<String> (); 101 row.add(start); 102 findLadderSequence(start, end, row, new HashSet<String> ()); 103 return result; 104 } 105 106 private static void findLadderSequence(String start, String end, ArrayList<String> row, HashSet<String> usedSet){ 107 if (start.equals(end)){ 108 result.add(row); 109 } 110 myTrieNode cur = trie.root; 111 myTrieNode next = cur; 112 for ( int i = 0; i < start.length(); i ++ ){ 113 char c = start.charAt(i); 114 for ( int j = 0; j < 26; j ++ ){ 115 if (cur.childList[j] != null && cur.childList[j].val == c){ 116 next = cur.childList[j]; 117 } 118 if (cur.childList[j] != null && cur.childList[j].val != c){ 119 String tmp = cur.childList[j].containsWord(start.substring(i + 1 )); 120 if (tmp != null && ! usedSet.contains(tmp)){ 121 row.add(tmp); 122 usedSet.add(tmp); 123 findLadderSequence(tmp, end, new ArrayList<String>(row), new HashSet<String> (usedSet)); 124 row.remove(row.size() - 1 ); 125 usedSet.remove(tmp); 126 } 127 } 128 } 129 cur = next; 130 } 131 132 } 133 134 private static myTrie generateTrie(Set<String> dict){ 135 myTrie trie = new myTrie(); 136 if (dict.isEmpty()) return trie; 137 // generate Trie for dictionary 138 Iterator it = dict.iterator(); 139 while (it.hasNext()){ 140 String word = (String)it.next(); 141 trie.insert(word); 142 } 143 return trie; 144 } 145 146 public static void main(String[] args){ 147 String start = "hit" ; 148 String end = "cog" ; 149 Set<String> dict = new HashSet<String> (); 150 dict.add("hot" ); 151 dict.add("dot" ); 152 dict.add("dog" ); 153 dict.add("lot" ); 154 dict.add("log" ); 155 findLadders(start, end, dict); 156 System.out.println(result); 157 } 158 }
Output:
[[hit, hot, dot, lot, log, cog],
[hit, hot, dot, lot, log, dog, cog],
[hit, hot, dot, dog, cog],
[hit, hot, dot, dog, log, cog],
[hit, hot, lot, dot, dog, cog],
[hit, hot, lot, dot, dog, log, cog],
[hit, hot, lot, log, cog],
[hit, hot, lot, log, dog, cog]]