Word Search II
給定2D木板和詞典中的單詞列表,請在木板中查找所有單詞。每個單詞必須由順序相鄰的單元格的字母構成,其中“相鄰”單元格是水平或垂直相鄰的單元格。同一字母單元在一個單詞中最多隻能使用一次。
Example:
Input:
board = [
['o','a','a','n'],
['e','t','a','e'],
['i','h','k','r'],
['i','f','l','v']
]
words = ["oath","pea","eat","rain"]
Output: ["eat","oath"]
Backtracking + Trie
Intuitively, start from every cell and try to build a word in the dictionary. Backtracking (dfs)
is the powerful way to exhaust every possible ways. Apparently, we need to do pruning
when current character is not in any word.
- 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.
"Mutil-LinkedList"
?
Combing them, Trie
is the natural choice. Notice that:
TrieNode
is all we need.search
andstartsWith
are useless.- No need to store character at TrieNode.
c.next[i] != null
is enough. - Never use
c1 + c2 + c3
. UseStringBuilder
. - No need to use
O(n^2)
extra spacevisited[m][n].
- No need to use
StringBuilder
. Storingword
itself at leaf node is enough. - No need to use
HashSet
to de-duplicate. Use “one time search” trie.
For more explanations, check out dietpepsi’s blog.
Code Optimization
UPDATE: Thanks to @dietpepsi we further improved from 17ms
to 15ms
.
59ms
: Usesearch
andstartsWith
in Trie class like this popular solution.33ms
: Remove Trie class which unnecessarily starts fromroot
in everydfs
call.30ms
: Usew.toCharArray()
instead ofw.charAt(i)
.22ms
: UseStringBuilder
instead ofc1 + c2 + c3
.20ms
: RemoveStringBuilder
completely by storingword
instead ofboolean
in TrieNode.20ms
: Removevisited[m][n]
completely by modifyingboard[i][j] = '#'
directly.18ms
: check validity, e.g.,if(i > 0) dfs(...)
, before going to the nextdfs
.17ms
: De-duplicatec - a
with one variablei
.15ms
: RemoveHashSet
completely. dietpepsi’s idea is awesome.
The final run time is 15ms
. Hope it helps!
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;
}
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 buildTrie(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;
}
原文鏈接:https://leetcode.com/problems/word-search-ii/discuss/59780/Java-15ms-Easiest-Solution-(100.00)