LeetCode - Word Search II
Problem description
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:
1
2
3
4
5
6
7
8
9
10
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"]
Note:
- All inputs are consist of lowercase letters a-z.
- The values of words are distinct.
Analysis
The basic idea is to use Trie. And I use the matrix to build a Trie, which needs a lot of memory.
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
class Solution {
public List<String> findWords(char[][] board, String[] words) {
Node root = new Node();
int max = 0;
for (String word:words){
max = Math.max(max, word.length());
}
constructTrie(board, root , max);
List<String> res = new ArrayList<>();
for (String word:words){
if (search(word, root)){
res.add(word);
}
}
return res;
}
void constructTrie(char[][] board, Node root, int maxDepth){
for(int i = 0; i< board.length; i++){
for (int j = 0; j< board[0].length; j++){
boolean[][] visited = new boolean[board.length][board[0].length];
dfs(board, i, j, root, maxDepth, visited);
}
}
}
int[][] dir = new int[][]{ {1,0},{0,1},{-1,0},{0,-1} };
void dfs(char[][] board, int i, int j, Node node, int left, boolean[][] visited){
if (left == 0 || i < 0 || j < 0 || i >= board.length || j >= board[0].length || visited[i][j])
{
node.isEnd = true;
return;
}
visited[i][j] = true;
char c = board[i][j];
if (node.children[c-'a'] == null){
node.children[c-'a'] = new Node();
}
for (int[] d:dir){
dfs(board, i+d[0], j+d[1], node.children[c-'a'], left-1, visited);
}
visited[i][j] = false;
}
boolean search(String word, Node root){
Node p = root;
for (char c:word.toCharArray()){
if (p.children[c-'a'] != null){
p = p.children[c-'a'];
}
else {
return false;
}
}
return true;
}
class Node{
Node[] children = new Node[26];
boolean isEnd = false;
}
}
We can use the words to build a Trie and use less memory.
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
class TrieNode {
HashMap<Character, TrieNode> children = new HashMap<Character, TrieNode>();
String word = null;
public TrieNode() {}
}
class Solution {
char[][] _board = null;
ArrayList<String> _result = new ArrayList<String>();
public List<String> findWords(char[][] board, String[] words) {
// Step 1). Construct the Trie
TrieNode root = new TrieNode();
for (String word : words) {
TrieNode node = root;
for (Character letter : word.toCharArray()) {
if (node.children.containsKey(letter)) {
node = node.children.get(letter);
} else {
TrieNode newNode = new TrieNode();
node.children.put(letter, newNode);
node = newNode;
}
}
node.word = word; // store words in Trie
}
this._board = board;
// Step 2). Backtracking starting for each cell in the board
for (int row = 0; row < board.length; ++row) {
for (int col = 0; col < board[row].length; ++col) {
if (root.children.containsKey(board[row][col])) {
backtracking(row, col, root);
}
}
}
return this._result;
}
private void backtracking(int row, int col, TrieNode parent) {
Character letter = this._board[row][col];
TrieNode currNode = parent.children.get(letter);
// check if there is any match
if (currNode.word != null) {
this._result.add(currNode.word);
currNode.word = null;
}
// mark the current letter before the EXPLORATION
this._board[row][col] = '#';
// explore neighbor cells in around-clock directions: up, right, down, left
int[] rowOffset = {-1, 0, 1, 0};
int[] colOffset = {0, 1, 0, -1};
for (int i = 0; i < 4; ++i) {
int newRow = row + rowOffset[i];
int newCol = col + colOffset[i];
if (newRow < 0 || newRow >= this._board.length || newCol < 0
|| newCol >= this._board[0].length) {
continue;
}
if (currNode.children.containsKey(this._board[newRow][newCol])) {
backtracking(newRow, newCol, currNode);
}
}
// End of EXPLORATION, restore the original letter in the board.
this._board[row][col] = letter;
// Optimization: incrementally remove the leaf nodes
if (currNode.children.isEmpty()) {
parent.children.remove(letter);
}
}
}
What to improve
- use less possible to build a Trie to save memory