Trie树
Trie树
1、用途
快速查询目标字符串在之前所有字符串中出现的次数
2、原理
将每个字符串看作有根树上的一条由根节点到另一非根节点节点的路径:除根节点以外每个节点都有一个与之对应的字符作为该点的权值,将路径所经过的点上的字符按次序排列得到原字符串。
在字符串结束的节点上打上标记,表示在该点处有字符串结束。
对于int型整数而言,可以把二进制位的0,1看作字符,同样可以利用trie存储。
如图,绿色节点代表该节点处有标记。
3、复杂度
插入:(O(|S|))
查询:(O(|S|))
4、模板
初始化
//字符型
const int MAXN = 1e5 + 10;
int son[MAXN][26], cnt[MAXN], idx;
//数字型
const int MAXN = 1e5 + 10;
int son[MAXN][2], cnt[MAXN], idx;
插入
//字符型
void insert(char *str)
{
int p = 0;
for (int i = 0; str[i]; ++i)
{
int u = str[i] - 'a';
if (!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
//数字型
void insert(int x)
{
int p = 0;
for (int i = 31; i >= 0; --i)
{
int u = x >> i & 1;
if (!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
查询
//字符型
int query(char *str)
{
int p = 0;
for (int i = 0; str[i]; ++i)
{
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
//数字型
int query(int x)
{
int p = 0;
for (int i = 31; i >= 0; --i)
{
int u = x >> i & 1;
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
5、备注
①MAXN的取值需要根据题目条件自行改动。字符型的上限一般是字符串总长度,数字型的上限一般是数字个数*32。
②Trie树不建议用类封装,因为数组往往会非常大。本地编译时容易爆栈。建议把所有数组放在全局变量中。
③如果非全局变量或有多组数据,使用前记得memset。
④这里的字符型仅包括小写字母。
⑤数字型的trie树在使用时往往需要变形。