声明
由于本文章其中一些代码是本人很久以前写的,而有一些是本人现在写的,所以码风有些不同,请勿介意。
基础知识
字典树(trie),又称前缀树(prefix tree)。顾名思义,就是一个存储字符串的树,其中用边来表示字母,用从根节点到树上的某一结点的路径来表示字符串,确定一个节点就能表示一个字符串。如图所示,路径0->1->2->3表示的字符串为ant。
字典树的表示
我们假设字符串仅由小写字母构成,则每个节点最多有 个子节点,因此我们可以这样表示:
int trie[1100000][26]
也可以这样表示:
struct Node{
int ch[26], cnt, siz;
}trie[1100000];
本章基于第二种表示方式展开。
字典树的基本应用
字典树的插入
void insert(string s)
{
int u = 0;//当前的节点
for (int i = 0; i < s.size(); ++ i)
{
int c = s[i] - 'a';
if (trie[u].ch[c] == 0){
trie[u].ch[c] = ++ tot;//tot为当前这个字典树的大小
}
u = trie[u].ch[c];
++ trie[u].siz;//将此结点的大小加一
}
++ trie[u].cnt;//标记此节点为某个字典树的结尾
}
查询字符串作为前缀的数量
int query(string pre)
{
int u = 0;
for (int i = 0; i < pre.size(); ++ i)
{
int c = pre[i] - 'a';
if (trie[u].ch[c] == 0)
{
return 0;
}
u = trie[u].ch[c];
}
return trie[u].siz;//直接输出当前子树的大小即可
}
以上两个操作的时间复杂度均为 ,其中
表示这个字符串的长度。
例题
例题1:公共前缀串
题面
讲解
简化题意,本题实际上就是再求任意字符串作为前缀的数量大于等于 时的最大长度。首先,我们一一将这一堆字符串插入到字典树当中,然后顺便记录前缀次数大于等于
的情况即可。
代码
#include<bits/stdc++.h>
using namespace std;
int q,tot,n,k,ans;
struct Node{
int ch[26],cnt,siz;
}trie[1100000];
void insert(string s){
int u=0;
for(int i=0;i<s.size();i++){
int c=s[i]-'a';
if(trie[u].ch[c]==0){
trie[u].ch[c]=++tot;
}
u=trie[u].ch[c];
trie[u].siz++;
if(trie[u].siz>=k){
ans=max(ans,i+1);
}
}
trie[u].cnt++;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>k;
string s;
while(n--){
cin>>s;
insert(s);
}
cout<<ans;
return 0;
}
例题2:字典树 II
题面
讲解
本题的操作一就是我们刚刚讲的插入,操作二则是查询字符串作为前缀的数量,我们只需要分不同情况调用这两个函数即可。
代码
#include<bits/stdc++.h>
using namespace std;
int q,tot;
struct Node{
int ch[26],cnt,siz;
}trie[1100000];
void insert(string s){
int u=0;
for(int i=0;i<s.size();i++){
int c=s[i]-'a';
if(trie[u].ch[c]==0){
trie[u].ch[c]=++tot;
}
u=trie[u].ch[c];
trie[u].siz++;
}
trie[u].cnt++;
}
int query(string pre){
int u=0;
for(int i=0;i<pre.size();i++){
int c=pre[i]-'a';
if(trie[u].ch[c]==0){
return 0;
}
u=trie[u].ch[c];
}
return trie[u].siz;
}
int main(){
ios::sync_with_stdio(false);
cin>>q;
while(q--){
int op;
cin>>op;
if(op==1){
string s;
cin>>s;
insert(s);
}else{
string s;
cin>>s;
cout<<query(s)<<endl;
}
}
return 0;
}