# Question

Formatted question description: https://leetcode.ca/all/212.html

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.

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

Constraints:
m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j] is a lowercase English letter.
1 <= words.length <= 3 * 104
1 <= words[i].length <= 10
words[i] consists of lowercase English letters.
All the strings of words are unique.

@tag-trie



# Algorithm

The solution to problem 79. Word Search can be reused here. Just search for each word in words and check whether it exists, and add the words that exist to the result list.

There also exist other solutions like Trie + DFS.

# Code

Trie

• import java.util.ArrayList;
import java.util.List;

public class Word_Search_II {

public static void main(String[] args) {
Word_Search_II out = new Word_Search_II();
Solution s = out.new Solution();

System.out.println(
s.findWords(
new char[][]{
{'o','a','a','n'},
{'e','t','a','e'},
{'i','h','k','r'},
{'i','f','l','v'} },
new String[]{"oath","pea","eat","rain"}
));

System.out.println(
s.findWords(
new char[][]{
{'a', 'b'},
{'a', 'a'} },
new String[]{"aba","baa","bab","aaab","aaa","aaaa","aaba"}
)); // output: ["aaa","aaab","aaba","aba","baa"]
}

/*

test case: huge data

board:
[["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"]]

words:
["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]

*/

class TrieNode {
public TrieNode[] children;
public String word;

public TrieNode() {
children = new TrieNode[26];
word = null;
for (int i = 0; i < 26; i++) {
children[i] = null;
}
}
}

class Trie {
TrieNode root;
/** Initialize your data structure here. */
public Trie() {
root = new TrieNode();
}

/** Inserts a word into the trie. */
public void insert(String word) {
char[] array = word.toCharArray();
TrieNode cur = root;
for (int i = 0; i < array.length; i++) {
if (cur.children[array[i] - 'a'] == null) {
cur.children[array[i] - 'a'] = new TrieNode();
}
cur = cur.children[array[i] - 'a'];
}
cur.word = word;
}
};

class Solution {
public List<String> findWords(char[][] board, String[] words) {
Trie trie = new Trie();

List<String> res = new ArrayList<>();
for (String word : words) {
trie.insert(word);
}
int rows = board.length;
if (rows == 0) {
return res;
}
int cols = board[0].length;

for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
dfs(board, i, j, trie.root, res);
}
}
return res;
}

private void dfs(char[][] board, int row, int col, TrieNode node, List<String> res) {
if (row < 0 || row >= board.length || col < 0 || col >= board[0].length) {
return;
}
char cur = board[row][col];

if (cur == '$' || node.children[cur - 'a'] == null) { return; } node = node.children[cur - 'a']; if (node.word != null) { res.add(node.word); node.word = null; } char temp = board[row][col]; board[row][col] = '$';
dfs(board, row - 1, col, node, res);
dfs(board, row + 1, col, node, res);
dfs(board, row, col - 1, node, res);
dfs(board, row, col + 1, node, res);
board[row][col] = temp;
}
}
}

• // OJ: https://leetcode.com/problems/word-search-ii/
// Time: O(WL + MN * 4^L) where M, N is the size of board, W is the size of words and L is the average length of word
// Space: O(WL)
struct TrieNode {
TrieNode *next[26] = {0};
bool word = false;
};
void addWord(TrieNode *node, string &w) {
for (char c : w) {
if (node->next[c - 'a'] == NULL) node->next[c - 'a'] = new TrieNode();
node = node->next[c - 'a'];
}
node->word = true;
}
class Solution {
int M, N, dirs[4][2] = { {0,1}, {0,-1}, {1,0}, {-1,0} };
vector<string> ans;
string path;
void dfs(vector<vector<char>> &A, TrieNode *node, int x, int y) {
char c = A[x][y];
node = node->next[c - 'a'];
if (!node) return;
path.push_back(c);
A[x][y] = 0;
if (node->word) {
ans.push_back(path);
node->word = false; // prevent adding the same word again.
}
for (auto &dir : dirs) {
int a = x + dir[0], b = y + dir[1];
if (a >= M || a < 0 || b >= N || b < 0 || A[a][b] == 0) continue;
dfs(A, node, a, b);
}
A[x][y] = c;
path.pop_back();
}
public:
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
if (board.empty() || board[0].empty()) return {};
M = board.size(), N = board[0].size();
TrieNode root;
for (auto &w : words) addWord(&root, w);
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) dfs(board, &root, i, j);
}
return ans;
}
};

• class Trie:
def __init__(self):
self.children = [None] * 26
self.w = ''

def insert(self, w):
node = self
for c in w:
idx = ord(c) - ord('a')
if node.children[idx] is None:
node.children[idx] = Trie()
node = node.children[idx]
node.w = w

class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
def dfs(node, i, j):
idx = ord(board[i][j]) - ord('a')
if node.children[idx] is None:
return
node = node.children[idx]
if node.w:
c = board[i][j]
board[i][j] = '0'
for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
x, y = i + a, j + b
if 0 <= x < m and 0 <= y < n and board[x][y] != '0':
dfs(node, x, y)
board[i][y] = c

trie = Trie()
for w in words:
trie.insert(w)
ans = set()
m, n = len(board), len(board[0])
for i in range(m):
for j in range(n):
dfs(trie, i, j)
return list(ans)

############

class Trie:
def __init__(self):
self.children = [None] * 26
self.w = ''

def insert(self, w):
node = self
for c in w:
idx = ord(c) - ord('a')
if node.children[idx] is None:
node.children[idx] = Trie()
node = node.children[idx]
node.w = w

class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
def dfs(node, i, j):
idx = ord(board[i][j]) - ord('a')
if node.children[idx] is None:
return
node = node.children[idx]
if node.w:
c = board[i][j]
board[i][j] = '0'
for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
x, y = i + a, j + b
if 0 <= x < m and 0 <= y < n and board[x][y] != '0':
dfs(node, x, y)
board[i][y] = c

trie = Trie()
for w in words:
trie.insert(w)
ans = set()
m, n = len(board), len(board[0])
for i in range(m):
for j in range(n):
dfs(trie, i, j)
return list(ans)

################3

class TrieNode:
def __init__(self, char):
self.neighbours = {}
self.isWord = False

class Trie:
def __init__(self):
self.root = TrieNode("-")

root = self.root
for i in range(0, len(word)):
c = word[i]
if c in root.neighbours:
root = root.neighbours[c]
else:
newnode = TrieNode(c)
root.neighbours[c] = newnode
root = root.neighbours[c]
root.isWord = True

class Solution:
# @param board, a list of lists of 1 length string
# @param words: A list of string
# @return: A list of string
def findWords(self, board, words):
trie = Trie()
res = []
visited = [[0] * len(board[0]) for i in range(0, len(board))]
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def dfs(i, j, board, visited, res, root, path):
if not root:
return

if root.isWord:
res.append(path)

for direction in directions:
ni, nj = i + direction[0], j + direction[1]
if 0 <= ni < len(board) and 0 <= nj < len(board[0]):
c = board[ni][nj]
if visited[ni][nj] == 0:
visited[ni][nj] = 1
dfs(ni, nj, board, visited, res, root.neighbours.get(c, None), path + c)
visited[ni][nj] = 0

for word in words:
root = trie.root
for i in range(0, len(board)):
for j in range(0, len(board[0])):
c = board[i][j]
visited[i][j] = 1
dfs(i, j, board, visited, res, root.neighbours.get(c, None), c)
visited[i][j] = 0
return list(set(res))



Java

• class Solution {
public List<String> findWords(char[][] board, String[] words) {
List<String> wordsInBoard = new ArrayList<String>();
for (String word : words) {
if (exist(board, word))
}
return wordsInBoard;
}

public boolean exist(char[][] board, String word) {
if (board == null || board.length == 0 || board[0].length == 0 || word == null || word.length() == 0)
return false;
int rows = board.length, columns = board[0].length;
boolean[][] visited = new boolean[rows][columns];
char startLetter = word.charAt(0);
List<int[]> startPositions = new ArrayList<int[]>();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (board[i][j] == startLetter)
}
}
for (int[] startPosition : startPositions) {
boolean exist = depthFirstSearch(board, word, startPosition, visited);
if (exist)
return true;
}
return false;
}

public boolean depthFirstSearch(char[][] board, String word, int[] startPosition, boolean[][] visited) {
return depthFirstSearch(board, word, startPosition, visited, 0);
}

public boolean depthFirstSearch(char[][] board, String word, int[] startPosition, boolean[][] visited, int startIndex) {
int rows = board.length, columns = board[0].length;
int length = word.length();
int startRow = startPosition[0], startColumn = startPosition[1];
if (startIndex == length - 1)
return board[startRow][startColumn] == word.charAt(startIndex);
if (board[startRow][startColumn] != word.charAt(startIndex))
return false;
visited[startRow][startColumn] = true;
int[][] directions = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
for (int[] direction : directions) {
int newRow = startRow + direction[0], newColumn = startColumn + direction[1];
if (newRow >= 0 && newRow < rows && newColumn >= 0 && newColumn < columns && !visited[newRow][newColumn]) {
int[] newPosition = {newRow, newColumn};
int newIndex = startIndex + 1;
if (depthFirstSearch(board, word, newPosition, visited, newIndex))
return true;
}
}
visited[startRow][startColumn] = false;
return false;
}
}

• // OJ: https://leetcode.com/problems/word-search-ii/
// Time: O(WL + MN * 4^L) where M, N is the size of board, W is the size of words and L is the average length of word
// Space: O(WL)
struct TrieNode {
TrieNode *next[26] = {0};
bool word = false;
};
void addWord(TrieNode *node, string &w) {
for (char c : w) {
if (node->next[c - 'a'] == NULL) node->next[c - 'a'] = new TrieNode();
node = node->next[c - 'a'];
}
node->word = true;
}
class Solution {
int M, N, dirs[4][2] = { {0,1}, {0,-1}, {1,0}, {-1,0} };
vector<string> ans;
string path;
void dfs(vector<vector<char>> &A, TrieNode *node, int x, int y) {
char c = A[x][y];
node = node->next[c - 'a'];
if (!node) return;
path.push_back(c);
A[x][y] = 0;
if (node->word) {
ans.push_back(path);
node->word = false; // prevent adding the same word again.
}
for (auto &dir : dirs) {
int a = x + dir[0], b = y + dir[1];
if (a >= M || a < 0 || b >= N || b < 0 || A[a][b] == 0) continue;
dfs(A, node, a, b);
}
A[x][y] = c;
path.pop_back();
}
public:
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
if (board.empty() || board[0].empty()) return {};
M = board.size(), N = board[0].size();
TrieNode root;
for (auto &w : words) addWord(&root, w);
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) dfs(board, &root, i, j);
}
return ans;
}
};

• class Trie:
def __init__(self):
self.children = [None] * 26
self.w = ''

def insert(self, w):
node = self
for c in w:
idx = ord(c) - ord('a')
if node.children[idx] is None:
node.children[idx] = Trie()
node = node.children[idx]
node.w = w

class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
def dfs(node, i, j):
idx = ord(board[i][j]) - ord('a')
if node.children[idx] is None:
return
node = node.children[idx]
if node.w:
c = board[i][j]
board[i][j] = '0'
for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
x, y = i + a, j + b
if 0 <= x < m and 0 <= y < n and board[x][y] != '0':
dfs(node, x, y)
board[i][y] = c

trie = Trie()
for w in words:
trie.insert(w)
ans = set()
m, n = len(board), len(board[0])
for i in range(m):
for j in range(n):
dfs(trie, i, j)
return list(ans)

############

class TrieNode:
def __init__(self, char):
self.neighbours = {}
self.isWord = False

class Trie:
def __init__(self):
self.root = TrieNode("-")

root = self.root
for i in range(0, len(word)):
c = word[i]
if c in root.neighbours:
root = root.neighbours[c]
else:
newnode = TrieNode(c)
root.neighbours[c] = newnode
root = root.neighbours[c]
root.isWord = True

class Solution:
# @param board, a list of lists of 1 length string
# @param words: A list of string
# @return: A list of string
def findWords(self, board, words):
trie = Trie()
res = []
visited = [[0] * len(board[0]) for i in range(0, len(board))]
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def dfs(i, j, board, visited, res, root, path):
if not root:
return

if root.isWord:
res.append(path)

for direction in directions:
ni, nj = i + direction[0], j + direction[1]
if 0 <= ni < len(board) and 0 <= nj < len(board[0]):
c = board[ni][nj]
if visited[ni][nj] == 0:
visited[ni][nj] = 1
dfs(ni, nj, board, visited, res, root.neighbours.get(c, None), path + c)
visited[ni][nj] = 0

for word in words:
root = trie.root
for i in range(0, len(board)):
for j in range(0, len(board[0])):
c = board[i][j]
visited[i][j] = 1
dfs(i, j, board, visited, res, root.neighbours.get(c, None), c)
visited[i][j] = 0
return list(set(res))