【每日一题】力扣211 添加与搜索单词-数据结构设计

文章目录

    • 题目
    • 解题思路
    • 代码(C++)
    • 总结


题目

题目链接:力扣211.添加与搜索单词-数据结构设计

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

实现词典类 WordDictionary

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

示例:

输入:
[“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'.' 或小写英文字母组成
  • 最多调用 104addWordsearch

解题思路

这题我首先想到的是直接用数组储存,再用暴力查找,结果超时了。查找代码如下:

bool search(string word) {
    for (int i = 0; i < str.size(); ++ i) {
        if (str[i] == word)
            return true;
        else {
            int j = word.size();
            if (j == str[i].size()) {
                int k;
                for (k = 0; k < j; ++ k) {
                    if (word[k] == '.')
                        continue;
                    if (word[k] != str[i][k])
                        break;
                }
                if (k == j)
                    return true;
            }
        }
    }
    return false;
}

这时就只能另寻他法了,题目提示的也很明确了:单词是小写字母组成。所以应该想到字典树了,没遇到过也没有关系,多做这类题就好了。

字典树

字典树和普通的树没什么区别,只是树的枝杈多了,树上的节点类型不一样而已。用字典树最通用或者说最经典的题是重复存储和查找字符串类型的,因为存储大量的字符串后再对其进行查找用暴力基本都会超时。由于字符串的字符量较少,所以可以用字典树优化代码。

就本题来说,本题的字典树就是 26 个字母为一层的树,树的第一层是 26 个字母数组,每个字母下面又有一个 26 个字母数组,依次下去。如果要查找某个字符串,可以直接的逐层的数组查找,时间复杂度只是需要查找的字符串的长度。

所以本题需要构建一个字典树,可以用结构体构建。

对于存储,当然是逐层把每个字符放入相应的数组里,直接一个 for 循环即可。

对于查找,可以用递归查找,每递归一次就是进入到下一层

  • 递归函数的返回值:返回是否查找到单词
  • 递归函数的参数一定有:需要查找的单词,存储单词的字典树,当前所在的层次
  • 递归函数的内容(执行代码):当前层次是否存在需要查找的单词的字符,存在则进入下一层,不存在返回 false 。如果是 '.'那么就可以直接进入下一层。

代码(C++)

struct Tree {
    vector<Tree *> child;
    bool end;
    Tree() {
        this->child = vector<Tree *>(26, nullptr);
        end = false;
    }
};

 class WordDictionary {
public:
    Tree *tree;
    WordDictionary() {
        tree = new Tree();
    }
    
    void addWord(string word) {
        Tree *node = tree;
        for (int i = 0; i < word.size(); ++ i) {
            char ch = word[i];
            if (node->child[ch - 'a'] == nullptr)
                node->child[ch - 'a'] = new Tree();
            node = node->child[ch - 'a'];
        }
        node->end = true;
    }
    
    bool search(string word) {
        return dfs(word, 0, tree);
    }

    bool dfs(string word, int n, Tree *node) {
        if (n == word.size())
            return node->end;
        char ch = word[n];
        if (ch >= 'a' && ch <= 'z') {
            if (node->child[ch - 'a'] != nullptr && dfs(word, n + 1, node->child[ch - 'a']))
                return true;
        }
        else if (ch == '.') {
            for (int j = 0; j < 26; ++ j) {
                if (node->child[j] != nullptr && dfs(word, n + 1, node->child[j]))
                    return true;
            } 
        }
        return false;
    }
};

总结

字典树的题还是需要掌握的,对于某些类型的题的优化是极大的,而且这种类型的题比较单一,大多可以直接看出题目是想考你字典树。字典树在项目上的使用也是比较多的,所以应该掌握这种题。

你可能感兴趣的:(每日一题,leetcode,算法,递归,每日一题)