-
Notifications
You must be signed in to change notification settings - Fork 117
Expand file tree
/
Copy pathWordSearchII212.java
More file actions
243 lines (211 loc) · 7.52 KB
/
WordSearchII212.java
File metadata and controls
243 lines (211 loc) · 7.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/**
* Given a 2D board and a list of words from the dictionary, find all words in
* the board.
*
* Each word must be constructed from letters of sequentially adjacent cell,
* where "adjacent" cells are those horizontally or vertically neighboring.
* The same letter cell may not be used more than once in a word.
*
* For example,
* Given words = ["oath","pea","eat","rain"] and board =
*
* [
* ['o','a','a','n'],
* ['e','t','a','e'],
* ['i','h','k','r'],
* ['i','f','l','v']
* ]
* Return ["eat","oath"].
* Note:
* You may assume that all inputs are consist of lowercase letters a-z.
*
* Hint:
* You would need to optimize your backtracking to pass the larger test. Could
* you stop backtracking earlier?
*
* If the current candidate does not exist in all words' prefix, you could stop
* backtracking immediately. What kind of data structure could answer such query
* efficiently? Does a hash table work? Why or why not? How about a Trie? If
* you would like to learn how to implement a basic trie, please work on this
* problem: Implement Trie (Prefix Tree) first.
*
*/
public class WordSearchII212 {
public List<String> findWords(char[][] board, String[] words) {
List<String> res = new ArrayList<>();
TrieNode root = buildTrie(words);
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
helper(board, i, j, root, res);
}
}
return res;
}
private void helper(char[][] board, int i, int j, TrieNode node, List<String> res) {
if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] == '#' || !node.containsKey(board[i][j])) {
return;
}
char curr = board[i][j];
node = node.get(curr);
if (node.containsWord()) {
res.add(node.getWord());
node.setWord(null);
}
board[i][j] = '#';
helper(board, i, j+1, node, res);
helper(board, i+1, j, node, res);
helper(board, i, j-1, node, res);
helper(board, i-1, j, node, res);
board[i][j] = curr;
}
public TrieNode buildTrie(String[] words) {
TrieNode root = new TrieNode();
for (String w : words) {
TrieNode node = root;
for (int i = 0; i < w.length(); i++) {
char c = w.charAt(i);
if (!node.containsKey(c)) {
node.put(c, new TrieNode());
}
node = node.get(c);
}
node.setWord(w);
}
return root;
}
class TrieNode {
// R links to node children
private TrieNode[] links;
private final int R = 26;
private String word;
public TrieNode() {
links = new TrieNode[R];
}
public boolean containsKey(char c) {
return links[c -'a'] != null;
}
public TrieNode get(char c) {
return links[c -'a'];
}
public void put(char c, TrieNode node) {
links[c -'a'] = node;
}
public void setWord(String w) {
word = w;
}
public String getWord() {
return word;
}
public boolean containsWord() {
return word != null;
}
}
private int[][] dirs = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public List<String> findWords2(char[][] board, String[] words) {
if (board == null || board.length == 0 || board[0].length == 0 || words.length == 0) return new ArrayList<>();
int M = board.length;
int N = board[0].length;
Trie trie = constructTrie(words);
Set<String> set = new HashSet<>();
boolean[][] visited = new boolean[M][N];
for (int i=0; i<M; i++) {
for (int j=0; j<N; j++) {
dfs(board, visited, trie, i, j, M, N, set);
}
}
return new ArrayList<>(set);
}
private void dfs(char[][] board, boolean[][] visited, Trie trie, int i, int j, int M, int N, Set<String> set) {
if (trie == null) return;
if (trie.word != null) set.add(trie.word);
if (i < 0 || j < 0 || i >= M || j >= N || visited[i][j]) return;
visited[i][j] = true;
char c = board[i][j];
Trie next = trie.get(c);
// if (next == null) return;
for (int[] d: dirs) {
dfs(board, visited, next, i+d[0], j+d[1], M, N, set);
}
visited[i][j] = false;
}
private Trie constructTrie(String[] words) {
Trie t = new Trie();
for (String word: words) t.addWord(word);
return t;
}
class Trie {
Trie[] children = new Trie[26];
String word = null;
public void addWord(String word) {
addWord(word.toCharArray(), 0);
}
public void addWord(char[] chars, int i) {
if (i == chars.length) {
word = new String(chars);
return;
}
if (children[chars[i]-'a'] == null) {
children[chars[i]-'a'] = new Trie();
}
children[chars[i]-'a'].addWord(chars, i+1);
}
public Trie get(char c) {
return children[c-'a'];
}
}
/**
* https://leetcode.com/problems/word-search-ii/discuss/59780/Java-15ms-Easiest-Solution-(100.00)
*
* 59ms: Use search and startsWith in Trie class like this popular solution.
* 33ms: Remove Trie class which unnecessarily starts from root in every dfs call.
* 30ms: Use w.toCharArray() instead of w.charAt(i).
* 22ms: Use StringBuilder instead of c1 + c2 + c3.
* 20ms: Remove StringBuilder completely by storing word instead of boolean in TrieNode.
* 20ms: Remove visited[m][n] completely by modifying board[i][j] = '#' directly.
* 18ms: check validity, e.g., if(i > 0) dfs(...), before going to the next dfs.
* 17ms: De-duplicate c - a with one variable i.
* 15ms: Remove HashSet completely. dietpepsi's idea is awesome.
*/
public List<String> findWords3(char[][] board, String[] words) {
List<String> res = new ArrayList<>();
TrieNode root = buildTrie2(words);
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
dfs (board, i, j, root, res);
}
}
return res;
}
public void dfs(char[][] board, int i, int j, TrieNode p, List<String> res) {
char c = board[i][j];
if (c == '#' || p.next[c - 'a'] == null) return;
p = p.next[c - 'a'];
if (p.word != null) { // found one
res.add(p.word);
p.word = null; // de-duplicate
}
board[i][j] = '#';
if (i > 0) dfs(board, i - 1, j ,p, res);
if (j > 0) dfs(board, i, j - 1, p, res);
if (i < board.length - 1) dfs(board, i + 1, j, p, res);
if (j < board[0].length - 1) dfs(board, i, j + 1, p, res);
board[i][j] = c;
}
public TrieNode buildTrie2(String[] words) {
TrieNode root = new TrieNode();
for (String w : words) {
TrieNode p = root;
for (char c : w.toCharArray()) {
int i = c - 'a';
if (p.next[i] == null) p.next[i] = new TrieNode();
p = p.next[i];
}
p.word = w;
}
return root;
}
class TrieNode {
TrieNode[] next = new TrieNode[26];
String word;
}
}