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.
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”]
Input: board = [[“a”,“b”],[“c”,“d”]], words = [“abcb”]
Output: []
From: LeetCode
Link: 212. Word Search II
Trie (Prefix Tree):
A Trie is a tree-like data structure that is used to store a dynamic set of strings. Tries are particularly useful for searches in dictionaries with a large number of words. Each node of the Trie represents a single character of a word, and the path from the root node to any node in the tree represents the prefix (part of a word) associated with that node.
Benefits of Trie:
DFS Backtracking:
Backtracking is a general algorithm used to find all (or some) solutions to computational problems by incrementally building candidates towards solutions and abandoning a candidate as soon as it is determined that it cannot be extended to a valid solution.
In the context of this problem, we use backtracking to traverse the board starting from each cell. For each cell, we explore in all four possible directions (up, down, left, right) to see if we can form a word present in the Trie.
Solution Steps:
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
typedef struct TrieNode {
struct TrieNode *children[26];
char *word;
} TrieNode;
TrieNode* createNode() {
TrieNode *node = malloc(sizeof(TrieNode));
for (int i = 0; i < 26; i++) {
node->children[i] = NULL;
}
node->word = NULL;
return node;
}
void insert(TrieNode *root, char *word) {
TrieNode *node = root;
for (int i = 0; word[i]; i++) {
int index = word[i] - 'a';
if (!node->children[index]) {
node->children[index] = createNode();
}
node = node->children[index];
}
node->word = word;
}
void backtrack(char **board, int boardSize, int* boardColSize, TrieNode *node, int i, int j, char **result, int *returnSize) {
if (i < 0 || i >= boardSize || j < 0 || j >= boardColSize[i] || board[i][j] == '#') {
return;
}
char c = board[i][j];
if (!node->children[c - 'a']) {
return;
}
node = node->children[c - 'a'];
if (node->word) {
result[*returnSize] = node->word;
(*returnSize)++;
node->word = NULL; // To avoid duplication
}
board[i][j] = '#'; // Mark as visited
backtrack(board, boardSize, boardColSize, node, i+1, j, result, returnSize);
backtrack(board, boardSize, boardColSize, node, i-1, j, result, returnSize);
backtrack(board, boardSize, boardColSize, node, i, j+1, result, returnSize);
backtrack(board, boardSize, boardColSize, node, i, j-1, result, returnSize);
board[i][j] = c; // Revert back
}
char** findWords(char** board, int boardSize, int* boardColSize, char **words, int wordsSize, int* returnSize) {
TrieNode *root = createNode();
for (int i = 0; i < wordsSize; i++) {
insert(root, words[i]);
}
char **result = malloc(wordsSize * sizeof(char*));
*returnSize = 0;
for (int i = 0; i < boardSize; i++) {
for (int j = 0; j < boardColSize[i]; j++) {
backtrack(board, boardSize, boardColSize, root, i, j, result, returnSize);
}
}
return result;
}