「前端刷题」211.添加与搜索单词 - 数据结构设计(MEDIUM)

104 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

题目(Design Add and Search Words Data Structure)

链接:https://leetcode-cn.com/problems/design-add-and-search-words-data-structure
解决数:666
通过率:50.4%
标签:深度优先搜索 设计 字典树 字符串 
相关公司:facebook amazon google 

请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。

实现词典类 WordDictionary :

  • WordDictionary() 初始化词典对象
  • void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配
  • bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回  false 。word 中可能包含一些 '.' ,每个 . 都可以表示任何一个字母。

 

示例:

输入:
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
输出:
[null,null,null,null,false,true,true,true]

解释:
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // 返回 False
wordDictionary.search("bad"); // 返回 True
wordDictionary.search(".ad"); // 返回 True
wordDictionary.search("b.."); // 返回 True

 

提示:

  • 1 <= word.length <= 25
  • addWord 中的 word 由小写英文字母组成
  • search 中的 word 由 '.' 或小写英文字母组成
  • 最多调用 104 次 addWord 和 search

思路

看代码


var WordDictionary = function () {
  this.root = Object.create(null);
};

WordDictionary.prototype.addWord = function (word) {
  let cur = this.root;
  for (const ch of word) {
    if (!cur[ch]) {
      cur[ch] = {};
    }
    cur = cur[ch];
  }
  cur.isWord = true;
};

WordDictionary.prototype.search = function (word, cur = this.root) {
  let ch = undefined;
  for (let i = 0; i < word.length; i++) {
    ch = word.charAt(i);
    if (ch === '.') {
/**
  当 ch === '.'
    if(cur还有其他的字符) {
      递归search
      if(search('.'所处层级的其他字符的剩余部分继续搜索)) {
        任意一个搜索成功返回true
      }
      没搜到返回false
    }
  其他部分与基础的Trie一致
**/
      for (const key in cur) {
          if (this.search(word.slice(i + 1, word.length), cur[key])) {
            return true;
          }
      }
      return false;
    } else if (!cur[ch]) {
      return false;
    }
    cur = cur[ch];
  }
  return Boolean(cur.isWord);
};

// 字典树
class TrieNode {
    constructor() {
        this.children = new Array(26).fill(0);
        this.isEnd = false;
    }
    insert(word) {
        let node = this;
        for (let i = 0; i < word.length; i++) {
            const ch = word[i];
            // 计算当前字符的索引
            const index = ch.charCodeAt() - 'a'.charCodeAt();
            // 若索引位置还没有单词,则在此新建字典树
            if (node.children[index] === 0) node.children[index] = new TrieNode();
            node = node.children[index];
        }
        // 结尾标记单词结束了
        node.isEnd = true;
    }
    getChildren() {
        return this.children;
    }
    isEnd() {
        return this.isEnd;
    }
}

class WordDictionary {
    constructor() {
        this.trieRoot = new TrieNode();
    }
    addWord(word) {
        this.trieRoot.insert(word);
    }
    search(word) {
        const dfs = (index, node) => {
            // 若索引长度等于单词数,说明遍历完了,返回isEnd
            if (index === word.length) return node.isEnd;

            const ch = word[index];
            if (ch !== '.') {
                // 当前字符是字母,必须保证一致
                const child = node.children[ch.charCodeAt() - 'a'.charCodeAt()];
                if (child && dfs(index + 1, child)) return true;
            } else {
                // 当前字符是点,点可以代表任何字符
                // 只要有一个子节点是true即可
                for (const child of node.children) {
                    if (child && dfs(index + 1, child)) return true;
                }
            }
            // 字符不存在,返回false
            return false;
        };
        return dfs(0, this.trieRoot);
    }
}