Word Search II
Word Search II:
Given an m x n board
of characters and a list of strings words
, return all words on the board.
Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.
Example 1:
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"]
Example 2:
Input: board = [["a","b"],["c","d"]], words = ["abcb"]
Output: []
Constraints:
m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j]
is a lowercase English letter.1 <= words.length <= 3 * 10^4
1 <= words[i].length <= 10
words[i]
consists of lowercase English letters.- All the strings of
words
are unique.
Try this Problem on your own or check similar problems:
Solution:
- Java
- JavaScript
- Python
- C++
class Solution {
class Trie {
public class TrieNode{
TrieNode[] children;
boolean isWord = false;
String word;
public TrieNode(){
children = new TrieNode[26];
}
}
TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word) {
TrieNode iter = root;
for(int i = 0; i < word.length(); ++i){
char letter = word.charAt(i);
if(iter.children[letter - 'a'] == null){
iter.children[letter - 'a'] = new TrieNode();
}
iter = iter.children[letter - 'a'];
}
iter.isWord = true;
iter.word = word;
}
}
public List<String> findWords(char[][] board, String[] words) {
Trie trie = new Trie();
List<String> result = new ArrayList<>();
for(String word: words){
trie.insert(word);
}
for(int i = 0; i < board.length; ++i){
for(int j = 0; j < board[0].length; ++j){
helper(board, i, j, trie.root, result);
}
}
return result;
}
private void helper(char[][] board, int x, int y, Trie.TrieNode root, List<String> result){
if(x < 0 || x == board.length || y < 0 || y == board[0].length || board[x][y] == '#' || root.children[board[x][y] - 'a'] == null){
return;
}
char c = board[x][y];
root = root.children[c - 'a'];
if(root.isWord){
result.add(root.word);
root.isWord = false;
}
board[x][y] = '#';
helper(board, x + 1, y, root, result);
helper(board, x, y + 1, root, result);
helper(board, x - 1, y, root, result);
helper(board, x, y - 1, root, result);
board[x][y] = c;
}
}
/**
* @param {character[][]} board
* @param {string[]} words
* @return {string[]}
*/
var findWords = function (board, words) {
const trie = new Trie();
const result = [];
for (const word of words) {
trie.insert(word);
}
for (let i = 0; i < board.length; i++) {
for (let j = 0; j < board[0].length; j++) {
helper(board, i, j, trie.root, result);
}
}
return result;
};
function helper(board, x, y, root, result) {
if (
x < 0 ||
x === board.length ||
y < 0 ||
y === board[0].length ||
board[x][y] === "#" ||
root.children[board[x][y].charCodeAt(0) - 97] == null
) {
return;
}
const c = board[x][y];
root = root.children[c.charCodeAt(0) - 97];
if (root.isWord) {
result.push(root.word);
root.isWord = false;
}
board[x][y] = "#";
helper(board, x + 1, y, root, result);
helper(board, x, y + 1, root, result);
helper(board, x - 1, y, root, result);
helper(board, x, y - 1, root, result);
board[x][y] = c;
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let iter = this.root;
for (let i = 0; i < word.length; i++) {
const letter = word.charAt(i);
if (iter.children[letter.charCodeAt(0) - 97] == null) {
iter.children[letter.charCodeAt(0) - 97] = new TrieNode();
}
iter = iter.children[letter.charCodeAt(0) - 97];
}
iter.isWord = true;
iter.word = word;
}
}
class TrieNode {
constructor() {
this.children = Array(26).fill(null);
this.isWord = false;
this.word = "";
}
}
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
trie = Trie()
result = []
for word in words:
trie.insert(word)
for i in range(len(board)):
for j in range(len(board[0])):
self.helper(board, i, j, trie.root, result)
return result
def helper(self, board, x, y, root, result):
if (
x < 0 or
x == len(board) or
y < 0 or
y == len(board[0]) or
board[x][y] == "#" or
root.children[ord(board[x][y]) - ord('a')] is None
):
return
c = board[x][y]
root = root.children[ord(c) - ord('a')]
if root.isWord:
result.append(root.word)
root.isWord = False
board[x][y] = "#"
self.helper(board, x + 1, y, root, result)
self.helper(board, x, y + 1, root, result)
self.helper(board, x - 1, y, root, result)
self.helper(board, x, y - 1, root, result)
board[x][y] = c
class Trie:
class TrieNode:
def __init__(self):
self.children = [None] * 26
self.isWord = False
self.word = None
def __init__(self):
self.root = self.TrieNode()
def insert(self, word):
iter = self.root
for i in range(len(word)):
letter = word[i]
if iter.children[ord(letter) - ord('a')] is None:
iter.children[ord(letter) - ord('a')] = self.TrieNode()
iter = iter.children[ord(letter) - ord('a')]
iter.isWord = True
iter.word = word
class Trie {
public:
struct TrieNode {
TrieNode* children[26];
bool isWord;
string word;
TrieNode() {
for (int i = 0; i < 26; i++) {
children[i] = nullptr;
}
isWord = false;
}
};
TrieNode* root;
Trie() {
root = new TrieNode();
}
void insert(string word) {
TrieNode* iter = root;
for (char letter : word) {
if (iter->children[letter - 'a'] == nullptr) {
iter->children[letter - 'a'] = new TrieNode();
}
iter = iter->children[letter - 'a'];
}
iter->isWord = true;
iter->word = word;
}
};
class Solution {
public:
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
Trie trie;
vector<string> result;
for (string word : words) {
trie.insert(word);
}
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board[0].size(); j++) {
helper(board, i, j, trie.root, result);
}
}
return result;
}
void helper(vector<vector<char>>& board, int x, int y, Trie::TrieNode* root, vector<string>& result) {
if (
x < 0 ||
x == board.size() ||
y < 0 ||
y == board[0].size() ||
board[x][y] == '#' ||
root->children[board[x][y] - 'a'] == nullptr
) {
return;
}
char c = board[x][y];
root = root->children[c - 'a'];
if (root->isWord) {
result.push_back(root->word);
root->isWord = false;
}
board[x][y] = '#';
helper(board, x + 1, y, root, result);
helper(board, x, y + 1, root, result);
helper(board, x - 1, y, root, result);
helper(board, x, y - 1, root, result);
board[x][y] = c;
}
};
Time/Space Complexity:
- Time Complexity: O(mn4^L) where
L
is the average/maximum length of a word in input arraywords
- Space Complexity: O(sum(w) + L)
sum(w)
for the trie, andL
for the maximum recursion stack while searching for words
Explanation:
We combine two approach Word Search (we also get the same time complexity because of trie) and Trie like in Implement Trie (Prefix Tree) or Longest Word in Dictionary (making the space complexity sum of all word lengths since we're holding all words in each word-ending node in the trie). There are a few tricks we have to add to make the solution functional:
- Note we're using
TrieNode
to keep on expanding the current word candidate instead of having a StringBuilder or string. This is way better option, since we can easily check if current word candidate is a prefix of any inserted word in trieroot.children[board[x][y] - 'a'] == null
(we're basically pruning the search) - Just like in Word Search we have special character
'#'
so that we don't have to create avisited
matrix - Note that different paths can lead to same words, so to remove duplicates one way is to use a Set, but we can again utilize the Trie and once we have found a word in the board, we do the deduplication in the trie by changing the flag
root.isWord = false;
. Next time we find the same word, the flagisWord
will befalse
so we will not add it in the result.