给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例:
输入: words = ["oath","pea","eat","rain"] and board = [ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ]
输出: ["eat","oath"] 说明: 你可以假设所有输入都由小写字母 a-z 组成。
提示:
你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯? 如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。
const dirs = [
[-1, 0],
[0, 1],
[1, 0],
[0, -1],
];
// 构建一个trie树,从而查找,思路很简单
// Y 最长的字符串长度
// X words的长度
// 字符串操作使用,trie树,算法时间复杂度O(4 * min(Y,mn)* mn)
// 空间复杂度 O(26Y)
export default (board, words) => {
let res = [];
const m = board.length;
const n = board[0].length;
const buildTrie = () => {
const root = {};
for (const w of words) {
let node = root;
for (const c of w) {
if (node[c] == null) {
node[c] = {};
}
node = node[c];
}
node.word = w;
}
return root;
};
const search = (node, x, y) => {
if (node.word != null) {
res.push(node.word);
node.word = null;
}
if (x < 0 || x >= m || y < 0 || y >= n) return;
const c = board[x][y];
if (c === "#" || node[board[x][y]] == null) return;
board[x][y] = "#"; // Mark visited
// 四个方向查找
for (const [dx, dy] of dirs) {
const i = x + dx;
const j = y + dy;
search(node[c], i, j);
}
board[x][y] = c; // Reset
};
const root = buildTrie();
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
search(root, i, j);
}
}
return res;
};