定义 链接到标题
字典树(Trie),是一个像字典一样的树,又称前缀树。
可以高效的查询某个字符串是否在一组给定的字符串中,或者说查询某个单词是否在字典中。
字典树的查询时间复杂度可以认为是 $O(l)$,其中 $l$ 为待查询单词的长度。
引入 链接到标题
字典树示意图:
可以发现,这棵字典树用边来代表字母,而根结点到树上面某一个节点的路径就代表一个字符串,例如 $1\rightarrow 4\rightarrow 8\rightarrow 12$ 表示的就是字符串 caa
,如果结点 $12$ 对应的 end_
字段的值为 $true$,说明 caa
是字典树中一个完整的字符串,否则只是一个前缀。
trie 的结构非常好懂,我们用 $\delta(u,c)$ 表示结点 $u$ 的 $c$ 字符指向的下一个结点,或着说是结点 $u$ 代表的字符串后面添加一个字符 $c$ 形成的字符串的结点。( $c$ 的取值范围和字符集大小有关,不一定是 $0\sim 26$ 。)
字典树的实现 链接到标题
这里放一个简单的前缀树的类的实现,
struct Trie {
int nex[10000][26], cnt; // nex 的第一维度表示前缀树的结点数量,与上面的图相对应
bool end[10000]; // 该结点结尾的字符串是否存在
void insert(char *s, int l) { // 插入字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) nex[p][c] = ++cnt; // 如果没有,就添加结点
p = nex[p][c];
}
exist[p] = 1;
}
bool find(char *s, int l) { // 查找字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) return 0;
p = nex[p][c];
}
return exist[p];
}
};
也可以使用 new
动态创建前缀树,例如:
class Trie {
private:
vector<Trie *> vec;
bool is_end;
public:
Trie() : vec(26), is_end(false) {
for (int i = 0; i < 26; ++i) {
vec[i] = nullptr;
}
}
void insert(string word) {
Trie *node = this;
for (int i = 0; i < word.size(); ++i) {
if (node->vec[word[i] - 'a'] != nullptr) {
node = node->vec[word[i] - 'a'];
} else {
node->vec[word[i] - 'a'] = new Trie();
node = node->vec[word[i] - 'a'];
}
}
node->is_end = true;
}
}
应用 链接到标题
字典查找 链接到标题
字典树的最经典的应用——查找一个字符串是否在“字典”中出现过。
745. Prefix and Suffix Search (Hard)
676. Implement Magic Dictionary (Medium)
01 字典树 链接到标题
将数的二进制表示看成一个字符串,就可以构建出字符集为 ${0, 1}$ 的字典树。
可以用于维护异或极值或者异或和。 421. Maximum XOR of Two Numbers in an Array (Medium)