数据结构(2)——字典树

声明

由于本文章其中一些代码是本人很久以前写的,而有一些是本人现在写的,所以码风有些不同,请勿介意。

基础知识

字典树(trie),又称前缀树(prefix tree)。顾名思义,就是一个存储字符串的树,其中用边来表示字母,用从根节点到树上的某一结点的路径来表示字符串,确定一个节点就能表示一个字符串。如图所示,路径0->1->2->3表示的字符串为ant。

字典树的表示

我们假设字符串仅由小写字母构成,则每个节点最多有 $26$ 个子节点,因此我们可以这样表示:

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;//直接输出当前子树的大小即可
}

 以上两个操作的时间复杂度均为 \Theta (\left | s \right |),其中 \left | s \right | 表示这个字符串的长度。

例题

例题1:公共前缀串

题面

讲解 

简化题意,本题实际上就是再求任意字符串作为前缀的数量大于等于 k 时的最大长度。首先,我们一一将这一堆字符串插入到字典树当中,然后顺便记录前缀次数大于等于 k 的情况即可。

代码

#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;
}

​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值