题目链接:力扣211.添加与搜索单词-数据结构设计
请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。
实现词典类 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
由 '.'
或小写英文字母组成addWord
和 search
这题我首先想到的是直接用数组储存,再用暴力查找,结果超时了。查找代码如下:
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
。如果是 '.'
那么就可以直接进入下一层。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;
}
};
字典树的题还是需要掌握的,对于某些类型的题的优化是极大的,而且这种类型的题比较单一,大多可以直接看出题目是想考你字典树。字典树在项目上的使用也是比较多的,所以应该掌握这种题。