Every day a Leetcode
题目来源:211. 添加与搜索单词 - 数据结构设计
解法1:字典树
字典树(前缀树)是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。前缀树可以用 O(∣S∣) 的时间复杂度完成如下操作,其中 ∣S∣ 是插入字符串或查询前缀的长度:
- 向字典树中插入字符串 word;
- 查询字符串 word 是否已经插入到字典树中。
根据题意,WordDictionary 类需要支持添加单词和搜索单词的操作,可以使用字典树实现。
对于添加单词,将单词添加到字典树中即可。
对于搜索单词,从字典树的根结点开始搜索。由于待搜索的单词可能包含点号,因此在搜索过程中需要考虑点号的处理。对于当前字符是字母和点号的情况,分别按照如下方式处理:
-
如果当前字符是字母,则判断当前字符对应的子结点是否存在,如果子结点存在则移动到子结点,继续搜索下一个字符,如果子结点不存在则说明单词不存在,返回 false;
-
如果当前字符是点号,由于点号可以表示任何字母,因此需要对当前结点的所有非空子结点继续搜索下一个字符。
重复上述步骤,直到返回 false 或搜索完给定单词的最后一个字符。
如果搜索完给定的单词的最后一个字符,则当搜索到的最后一个结点的 isEnd 为 true 时,给定的单词存在。
特别地,当搜索到点号时,只要存在一个非空子结点可以搜索到给定的单词,即返回 true 。
代码:
/** @lc app=leetcode.cn id=211 lang=cpp** [211] 添加与搜索单词 - 数据结构设计*/// @lc code=start
struct TrieNode
{vector<TrieNode *> child;bool isEnd;TrieNode() : child(vector<TrieNode *>(26, nullptr)), isEnd(false) {}
};void insert(TrieNode *root, const string &word)
{TrieNode *node = root;for (const char &c : word){if (node->child[c - 'a'] == nullptr)node->child[c - 'a'] = new TrieNode();node = node->child[c - 'a'];}node->isEnd = true;
}class WordDictionary
{
private:TrieNode *trie;public:WordDictionary() : trie(new TrieNode()) {}void addWord(string word){insert(trie, word);}bool search(string word){return dfs(word, 0, trie);}bool dfs(const string &word, int index, TrieNode *node){if (index == word.size())return node->isEnd;char ch = word[index];if (ch >= 'a' && ch <= 'z'){TrieNode *child = node->child[ch - 'a'];if (child != nullptr && dfs(word, index + 1, child))return true;}else if (ch == '.'){for (int i = 0; i < 26; i++){TrieNode *child = node->child[i];if (child != nullptr && dfs(word, index + 1, child))return true;}}return false;}
};/*** Your WordDictionary object will be instantiated and called as such:* WordDictionary* obj = new WordDictionary();* obj->addWord(word);* bool param_2 = obj->search(word);*/
// @lc code=end
结果:
复杂度分析:
时间复杂度:初始化为 O(1),添加单词为 O(∣S∣),搜索单词为 O(∣Σ∣∣S∣),其中 ∣S∣ 是每次添加或搜索的单词的长度,Σ 是字符集,这道题中的字符集为全部小写英语字母,∣Σ∣=26。
空间复杂度:O(∣T∣⋅∣Σ∣),其中 ∣T∣ 是所有添加的单词的长度之和,Σ 是字符集,这道题中的字符集为全部小写英语字母,∣Σ∣=26。