212. Word Search II
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.
Example:
Input:
words = ["oath","pea","eat","rain"] and board =
[
['o','a','a','n'],
['e','t','a','e'],
['i','h','k','r'],
['i','f','l','v']
]
Output: ["eat","oath"]
Note:
You may assume that all inputs are consist of lowercase letters a-z
.
Thoughts
How do we instantly know the current character is invalid? -> hashmap?
How do we instantly know what's the next valid character? -> LinkedList
But the next character can be chosen from a list of characters. -> "Multi-LinkedList?"
Combing them,
Trie
is the natural choice. Notice that:No need to store character at TrieNode:
c.next[i] != null
is enough.No need to use O(n^2) extra space visited[m][n]
No need to use
StringBuilder
. Storingword
itself at leaf node is enoughNo need to use
Hashset
to de-duplicate. Set "cur.word = null" each time when found a character during DFS
Optimization:
w.toCharArray() -> w.charAt(i)
visited[m][n] -> board[i][j] = '#'
checking index validity before calling dfs
[c - ''a'] to i
Time Complexity
O( l * wl) - Build the Tree
O(min(m * n, wl) * l): worse case cenario. However, if words starting with the same characters and path sharing the cells, Trie can check multiple words when DFS from a certain cell, rather than check only one word when DFS from a certain cell like the naive way - Search
Space Complexity:
O(l * wl) = max(O(wl), O(l * wl)) where:
O(wl) recursive call
O(l * wl) In the worst case when all words start with different characters, the trie has l * wl nodes. Also, since each word is stored in a leaf node, all the leaf nodes require l * wl memory (can be augmented by only store a boolean, but need to further discuss how to add found results in res), but the number of nodes stored in the tree is still l * wl;
Code:
class Solution {
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++){
dfs(board,i,j ,root, res);
}
}
return res;
}
private void dfs(char[][] board, int i, int j, TrieNode cur, List<String> res){
char c = board[i][j];
if(c == '#' || cur.next[c - 'a'] == null) return;
cur = cur.next[c-'a'];
if(cur.word != null){
res.add(cur.word);
cur.word = null; // de-duplicate
}
board[i][j] = '#'; // visited
if (i > 0) dfs(board, i - 1, j, cur, res);
if (j > 0) dfs(board, i, j - 1, cur, res);
if (i + 1< board.length) dfs(board, i + 1, j , cur, res);
if (j + 1< board[0].length) dfs(board, i, j + 1, cur, res);
board[i][j] = c;
}
private TrieNode buildTrie(String [] words){
TrieNode root = new TrieNode();
for(String word: words){
TrieNode cur = root;
for (char c: word.toCharArray()){
if(cur.next[c - 'a']== null) cur.next[c - 'a'] = new TrieNode();
cur = cur.next[c - 'a'];
}
cur.word = word;
}
return root;
}
}
class TrieNode{
TrieNode [] next = new TrieNode[26];
String word;
}
Reference to this post