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]]

