trie的作用是快速的查询和存储字符串集合。实现就是根节点之后查询字符有就继续没有就增加。同时标记结尾。
存储和查找示意图:
实现
#include<iostream>
using namesapce std;
const int N=100010;
int son[N][26],cnt[N],idx;
char str[N];
//插入函数
viod insert(char str[ ]){
int p=0;
for(int i=0;str[i];i++]){//str[]的结尾是/0
int u=str[i]-'a';//0~25把字母转换成数字,u现在存放的是插入的字符
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 main(){
//主函数
}
idx中存储当前用到了那个节点。son[x ] 中存储的是儿子们,cnt中存储的是以x为结尾的点的个数。
分析插入过程
son这个二维数组之中存储的就是tire数中所包含的字符串,在插入的函数中,首先就是循环读入字符串,要是不存在这个字符的话就加上,idx表示的是当前使用的节点,就是已经是第几个儿子了,要是没有的话就添加,比如一开始是一个空的树,现在要在这个树中加入abc,先进行循环,读入a字符,a字符是不存在的,因此进入了if条件判断,这个时候idx首先是+1变成1;赋值给son[0][a](a这个时候已经转化成相应的数字,这里写a是为了更加的直观),然后p=1,进行下一个循环,读入字符b,b也是不存在的,就进入if的条件判断,son[1][b]=2;出来之后再更新p的值,p=2;在进行循环读入字符c,同样son[2][c]=3;p=3; 读入结束,此时son中存储完第一个单词,son[0][a]=1;son[1][b]=2;son=[2][c]=3; cnt[3]=1接下来进行插入ab的话,首先进行循环的读入,a读入之后,son[0][a]是存在的,因此不进入if的条件判断,p=son[0][a]=1;同理b读入之后son[1][b]=2;cnt[2]=1;
这样就完成了操作。从上述过程分析可知idx中更新的是第几个儿子。
分析查找过程
同样的,要是不存在直接返回0,要是存在的话就把自己是第几个儿子赋值给p,最终判断p是不是一个结尾就行了。
#include<iostream>
#include<cstring>
using namespace std;
const int N=100010;
int son[N][26],cnt[N],idx;//cnt以当前这个结尾的有多少个,下标是0的点,既是根节点又是空节点
char str[N];
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]++;
}
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];//在此处把p更新成了自己的儿子
}
return cnt[p];
}
int main(){
int n;
scanf("%d",&n);
while(n--){
char op[2];
scanf("%s%s",op,str);
if(op[0]=='I') insert(str);
else printf("%d\n",query(str));
}
return 0;
}