介绍
Trie树主要是用来存储和查找字符串的,也叫作字典树,它是一种以空间换时间的数据结构。它的本质就是把多个字符串的公共前缀保存到一起,利用这这种数据结构可以很高效地进行存储和查找操作了。
案例
拿一个例子来说吧,比如说下面这幅图:
插入“abcd”,“abcc”,“abda”,“bcdc”,“bcda”,“bcdd”,“bcd”这几个字符串,它的步骤是这样的:
-
创建数组son[ ] [ ] , 遍历每个字符串,每个字符串一个字符一个字符遍历
-
查询son[0]这一行有没有第一个字符,如果没有,就保存下来,如果没有,son就进入下一行,继续遍历下一个字符
-
一直直到查询到末尾,然后给这个末尾这个地方打一个标记,表示有这么一个字符串
查询操作跟上面的像,步骤:
-
在son数组中从第一个字符开始查询,如果有匹配的,就继续向下查询,如果没有,就直接返回0,表示不存在
-
当查询到最后,说明存在,返回已经插入的该字符串的数目
题目:
下面拿一道题目来说:
维护一个字符串集合,支持两种操作:
I x
向集合中插入一个字符串 xx;
Q x
询问一个字符串在集合中出现了多少次。共有 N 个操作,输入的字符串总长度不超过 10^5,字符串仅包含小写英文字母。
输入格式
第一行包含整数 N,表示操作数。
接下来 N 行,每行包含一个操作指令,指令为
I x
或Q x
中的一种。输出格式
对于每个询问指令
Q x
,都要输出一个整数作为结果,表示 x 在集合中出现的次数。每个结果占一行。
数据范围
1≤N≤2∗10^4
输入样例:
5 I abc Q abc Q ab I ab Q ab
输出样例:
1 0 1
分析:
数据范围在10^4,所以我们可以创建常量 N = 100010,这道题目说了字符串仅包含小写英文字符,所以可以创建一个int类型的数组son[ N] [ 26 ]来保存字符,用cnt[N]来保存某个字符串插入的数目,还要用一个变量idx来设置保存进son数组中的值,因为只有这样才能找到下一行该去的地方,具体的可以见下面的代码
代码:
#include<iostream>
using namespace std;
const int N = 100010;
int son[N][26]; //这个数组使用了存储字符串的
int cnt[N]; // 这个数组是用来保存某个字符串出现的次数的
int idx; // 下标是0的点,既是根节点,也是空节点,每当son数组中要存储字符的时候,保存idx
char str[N];
void insert(string str){
int p = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - 'a'; // 将 a ~ z 映射为 0 ~ 25
if(!son[p][u]) son[p][u] = ++idx; // 如果在第p排的第u个位置上没有存储,就把idx存储
p = son[p][u]; // 让p继续向后走
}
cnt[p]++;
}
int query(string str){
int p = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - 'a';
if(!son[p][u]) return 0; // 如果son[p][u]为空,说明不存在该字符串,返回0
p = son[p][u];
}
return cnt[p]; // 所有的字符串都在cnt数组中存储,比如说存储了“abcdef”,没有存储“abcd”,你查找“abcd”是找不到的,每当存储一个字符串都会在末尾打个标记表示存了这么一个字符串
}
int main(){
int n;
cin >> n;
while(n--){
char op;
cin >> op >> str;
if(op == 'I') insert(str);
else cout << query(str) << endl;
}
return 0;
}
以上就是本次介绍的Trie数了,如果有错误或者不足欢迎指出。