Problem
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: []
Intuition
The task is to find all words on the board that are present in the given list of words. We can use a Trie data structure to efficiently check whether a sequence of letters on the board forms a valid word. The Trie is built from the list of words, and a depth-first search (DFS) is performed on the board to explore possible word formations.
Approach
TrieNode Class:
Implement a TrieNode class with attributes:
children: A dictionary mapping characters to child nodes.
iswords: A boolean indicating whether the current node represents the end of a word.
TrieNode Method:
Implement an addword method in the TrieNode class to insert a word into the Trie.
Solution Class:
Implement a Solution class with a findWords method.
Initialize an empty Trie (root) and add each word from the list of words to the Trie.
DFS Function:
Implement a DFS function (dfs) to explore possible word formations on the board.
The DFS function takes parameters (r, c, node, word), where (r, c) represents the current position on the board, node represents the current node in the Trie, and word represents the sequence of letters formed so far.
Explore adjacent cells horizontally and vertically, checking if the next letter in the sequence is a valid child of the current Trie node. If yes, continue the DFS.
If the current Trie node represents the end of a word, add the word to the result set (res).
Main Function:
Iterate over each cell on the board and start the DFS from that cell, considering it as the starting point of a word.
Return the list of words present in the result set.
Complexity
- Time complexity:
Trie Construction: The time complexity of Trie construction is O(w * m), where w is the number of words and m is the average length of the words. Each word of length m is inserted into the Trie.
DFS on Board: The time complexity of DFS on the board is O(rows * cols * 4^m), where rows and cols are the dimensions of the board, and m is the maximum length of a word. In the worst case, we explore 4 directions (horizontal and vertical) at each cell.
- Space complexity:
Trie Construction: The space complexity of Trie construction is O(w * m), where w is the number of words and m is the average length of the words. Each character of each word is stored in the Trie.
DFS on Board: The space complexity of DFS on the board is O(m), where m is the maximum length of a word. This is the maximum depth of the recursion stack during DFS. The visit set is used to keep track of visited cells on the board.
Code
class TrieNode:
def __init__(self):
self.children = {}
self.iswords = False
def addword(self, words):
cur = self
for c in words:
if c not in cur.children:
cur.children[c] = TrieNode()
cur = cur.children[c]
cur.iswords = True
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
root = TrieNode()
for w in words:
root.addword(w)
rows, cols = len(board), len(board[0])
res, visit = set(), set()
def dfs(r, c, node, word):
if(r < 0 or c < 0 or
r == rows or c == cols or
(r, c) in visit or board[r][c] not in node.children):
return
visit.add((r, c))
node = node.children[board[r][c]]
word += board[r][c]
if node.iswords:
res.add(word)
dfs(r + 1, c, node, word)
dfs(r - 1, c, node, word)
dfs(r, c - 1, node, word)
dfs(r, c + 1, node, word)
visit.remove((r, c))
for r in range(rows):
for c in range(cols):
dfs(r, c, root, '')
return list(res)