字典树(Trie树)详解
理论模块:
trie 树
字典树是一种用于实现字符串快速检索的多叉树结构
trie 的每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到一个字符 c c c
就沿着当前节点的 c c c 字符指针,走向该指针指向的节点
下图即为一个简易版字典树,存储了单词:abc
、bac
、abd
实现
(1) 初始化
一颗空 trie 仅包含一个根节点,该字符的指针均指向空
(2) 插入
当需要插入一个字符串 s s s 时,我们令一个指针 P P P 起初指向根节点。然后依次扫描 s s s 中的每一个字符 c c c
-
1.若 P P P 的 c c c 字符指针指向一个已经存在的节点 Q Q Q ,则令 P = Q P=Q P=Q
-
2.若 P P P 的 c c c 字符指针指向空,则新建一个节点 Q Q Q 令 P P P 的 c c c 字符指针指向 Q Q Q ,然后令 P = Q P=Q P=Q
-
当 S S S 中的字符扫描完毕,在当前节点 P P P 上标记它是一个字符串的末尾
(3) 检索
当需要检索一个字符串 S S S 在 T r i e Trie Trie 中是否存在时,我们令一个指针 P P P 起初指向根节点,然后依次扫描 S S S 中的每个字符 c c c
-
(1) 若 P P P 的 c c c 字符指针指向空,则说明 S S S 没有被插入到 T r i e Trie Trie 树中,结束检索
-
(2) 若 P P P 的 c c c 字符指针指向一个已经存在的节点 Q Q Q ,则令 P = Q P=Q P=Q
当 S S S 中的字符扫描完毕
若在当前节点 P P P 被标记为一个 字符串的末尾,则说明 S 在 Trie 树中存在,否则说明 S 没有被插入过
#include<bits/stdc++.h>
using namespace std;
int trie[ ][ ],tot=1;
char str[ ];
bool end[ ];
void put()
{//1 int len;//2int p=1;//3for(int i=1;i<=len;i++){int ch=str[i]-'a';trie[p][ch]//(1)指向空trie[p][ch]=++tot;//(2)指向已存在p=trie[p][ch]; }//4end[p]=true;return;
}
bool find()
{//1 int len;//2int p=1;//3for(int i=1;i<=len;i++){int ch=str[i]-'a';trie[p][ch]//(1)指向空return false;//(2)指向已存在p=trie[p][ch]; }//4return end[p];
}
int main(){return 0;
}
例题:luoguP8306[模板]字典树
分析:
给定 n n n 个模式串 1,2,…, s 1 s_1 s1, s 2 s_2 s2,…, s n s_n sn 和 q 次询问,每次询问给定一个文本串 t i t_i ti,请回答 1∼ s 1 s_1 s1∼ s n s_n sn 中有多少个字符串 s j s_j sj 满足 t i t_i ti 是 s j s_j sj 的前缀。
那么很明显,这道题主要考的就是我们对于字典树的运用。
除了上文所述的存储方法,还可以运用与其他树形结构相类似的结构体存储:
struct NODE{int cnt=0;int son[65];
}node[MAXN];
不过字符还需要转换成一个数字,这里我们就需要用到一个类似于map映射的东西:
int getnumber(char ch)
{if(ch>='A'&&ch<='Z')return ch-'A';if(ch>='a'&&ch<='z')return ch-'a'+26;return ch-'0'+52;
}//约等于map映射
插入操作:
void insert(string s)//插入操作,s表示需要插入的字符串
{now=start;for(int i=0;i<s.size();i++){num=getnumber(s[i]);if(node[now].son[num]==0)node[now].son[num]=++cnt;node[now].cnt++;now=node[now].son[num];}node[now].cnt++;return;
}
查询操作:
void find(string s)
{now=start;for(int i=0;i<s.size();i++){num=getnumber(s[i]);if(node[now].son[num]==0){printf("0\n");return;}now=node[now].son[num];}printf("%d\n",node[now].cnt);return;
}
整个代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e6+5;
int num,now,start,cnt;
int T,n,q;
string s;
int getnumber(char ch)
{if(ch>='A'&&ch<='Z')return ch-'A';if(ch>='a'&&ch<='z')return ch-'a'+26;return ch-'0'+52;
}//约等于map映射
struct NODE{int cnt=0;int son[65];
}node[MAXN];
void insert(string s)//插入操作,s表示需要插入的字符串
{now=start;for(int i=0;i<s.size();i++){num=getnumber(s[i]);if(node[now].son[num]==0)node[now].son[num]=++cnt;node[now].cnt++;now=node[now].son[num];}node[now].cnt++;return;
}
void find(string s)
{now=start;for(int i=0;i<s.size();i++){num=getnumber(s[i]);if(node[now].son[num]==0){printf("0\n");return;}now=node[now].son[num];}printf("%d\n",node[now].cnt);return;
}
int main(){scanf("%d",&T);while(T--){scanf("%d%d",&n,&q);while(n--){cin >> s;insert(s);}while(q--){cin >> s;find(s);}start=++cnt;}return 0;
}