携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
题目(Word Search II)
链接:https://leetcode-cn.com/problems/word-search-ii
解决数:694
通过率:45.2%
标签:字典树 数组 字符串 回溯 矩阵
相关公司:amazon uber microsoft
给定一个 m x n 二维字符网格 board ****和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。
单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例 1:
输入: board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出: ["eat","oath"]
示例 2:
输入: board = [["a","b"],["c","d"]], words = ["abcb"]
输出: []
提示:
m == board.lengthn == board[i].length1 <= m, n <= 12board[i][j]是一个小写英文字母1 <= words.length <= 3 * 1041 <= words[i].length <= 10words[i]由小写英文字母组成words中的所有字符串互不相同
思路
- 三部分:单词表 → 哈希表 → 前缀树。二维网络 → 递归 → 找前缀树中的单词。剪枝
- 剪枝:
pre存上节点len存分支数。从找到单词的节点向上找最后1个分支数为1分支,删除键名
代码
var findWords = function(board, words) {
const h = {} // 前缀表
const r = [] // 结果集数组
/** 单词表 → 哈希表 → 前缀树 **/
for (const w of words) {// 遍历单词表,用哈希表组装前缀树
for(var i = 0, t = h, p; i < w.length; i++) {
if (!t[w[i]]) {
t[w[i]] = Object.create(null) // 创建一个不含_proto_的纯净对象作节点,比{}提升性能
t[w[i]].len = 0 // 初始节点长度设为`0`,后面会`+1`
}
p = t // 存前缀树上一级节点
t = t[w[i]] // 移到当前节点
t.pre = p // 当前节点的`pre`属性,指向上一级节点
t.len++ // 单词共享前缀的情况,节点长度`+1`。在剪枝时,通过pre向上查找,直到len != 1,删除单词在前缀树的独占部分
}
t.word = w // 当前单词的前缀树最后一个字母,添加单词结尾标记,内容就是 单词本身
}
/** 剪枝 **/
const s = (h, p) => {// 找到一个单词,就删掉 这个单词所在的前缀树,但不能影响其他单词,所以只删节点长度1(该单词独占)
while(h.len === 1) p = h, h = h.pre // 从当前节点层层向上,直到节点长度不为1,存在分支
for(var k in p) delete p[k]// p暂存最后一个节点长度为1的节点,把p从前缀树剪掉
}
/** 二维网络 → 递归 → 找前缀树中的单词 **/
const d = (i, j, h) => { // 递归
var t = board[i] && board[i][j] // 暂存单元格字母
if (t === false || h[t] === undefined) return // 字母不存在(逾界) 或 字母不再前缀表
h = h[t] // 字母在前缀表,向下找后面的节点
if (h.word) { // 该字母是不是某个单词的结尾(组装前缀表时,把单词结尾标记 设为 单词本身)
r.push(h.word) // 将单词放入结果集
h.word = '' // 当单词结尾标记置空,避免这个单词被重复找到
s(h) // 剪枝,传入前缀树当前节点
}
board[i][j] = '' // 当前格子找过了,值清空,继续递归不再找
d(i - 1, j, h), d(i + 1, j, h), d(i, j - 1, h), d(i, j + 1, h) // 从当前格子开始,向四周继续递归
board[i][j] = t // 回溯时,将当前格子值复原
}
for (let i = board.length; i--;) {// 遍历二维网格,将每个格子作为开始前缀,在前缀树找完整单词
for (let j = board[0].length; j--;) {
d(i, j, h)
}
}
return r
};