Word Search II

HardArrayStringBacktrackingTrieMatrix

Solution

export function findWords(board: string[][], words: string[]): string[] {
  const answer = new Set<string>();
 
  const trie = new Trie(words);
 
  const [n, m] = [board.length, board[0].length];
  const dfs = (currentNode: TrieNode, y = 0, x = 0, currentPath = '') => {
    if (currentNode.isEnd) {
      answer.add(currentPath);
      currentNode.isEnd = false;
    }
    if (y < 0 || n <= y || x < 0 || m <= x || board[y][x] === '#') return;
 
    const char = board[y][x];
    const nextNode = currentNode.find(char);
    if (nextNode === undefined) return;
 
    board[y][x] = '#';
    dfs(nextNode, y + 1, x, currentPath + char);
    dfs(nextNode, y, x + 1, currentPath + char);
    dfs(nextNode, y - 1, x, currentPath + char);
    dfs(nextNode, y, x - 1, currentPath + char);
    board[y][x] = char;
    return;
  };
 
  const rootNode = trie.rootNode;
 
  for (let y = 0; y < n; y++) {
    for (let x = 0; x < m; x++) {
      dfs(rootNode, y, x);
    }
  }
 
  return [...answer].sort();
}
 
class TrieNode {
  public isEnd: boolean;
  private children: Map<string, TrieNode>;
 
  constructor() {
    this.isEnd = false;
    this.children = new Map();
  }
 
  find(char: string): TrieNode | undefined {
    return this.children.get(char);
  }
 
  create(char: string): TrieNode {
    const newNode = new TrieNode();
    this.children.set(char, newNode);
    return newNode;
  }
 
  findOrCreate(char: string): TrieNode {
    const childNode = this.find(char);
    if (childNode) {
      return childNode;
    }
    return this.create(char);
  }
}
 
class Trie {
  public rootNode: TrieNode;
 
  constructor(words: string[]) {
    this.rootNode = new TrieNode();
    for (const word of words) {
      this.insert(word);
    }
  }
 
  insert(word: string) {
    let currentNode = this.rootNode;
    for (const char of word) {
      currentNode = currentNode.findOrCreate(char);
    }
    currentNode.isEnd = true;
  }
}