Trie 入门例题汇总

Trie(字典树)是一种实现字符串快速检索的多叉树结构。Trie的每个节点都拥有若干个字符指针,若在插入或检索时扫描到一个字符c,就沿着当前的节点的c字符指针,走向该指针指向的节点 。
Trie结构一种典型的用空间换取时间的数据结构,其空间复杂度为O(NC),其中N代表节点的个数,C代表着字符集的大小。
一:对字符串的处理
例题:前缀统计
题意:给定n个字符串,再给出m个询问 每次询问一个字符串T,问s1~sn中有多字符串是他的前缀
思路:插入字符串的时候,没插入完成就将以当前节点为结尾的字符串数量++,再从头到尾的询问T字符串即可。

#include <bits/stdc++.h>
// trie 模板
using namespace std;
typedef long long ll;
const int MAXN = 1e6+7;
const int N = 5e5+7;
char str[MAXN];
int t[N][26],cnt[N],idx;//cnt数组保存以当前节点为结尾的字符串有几个
void insert(char *s)
{
	int len = strlen(s);
	int p = 0;
	for(int i = 0;i < len;i ++){
		if(!t[p][s[i]-'a']) t[p][s[i]-'a'] = ++idx;//这个idx就相当有我这个虚拟数组的分配的标号
		p = t[p][s[i]-'a'];
	}
	cnt[p]++;//当前大小++
}

int query(char *s)
{
	int len = strlen(s),p = 0,ans = 0;
	for(int i = 0;i < len;i ++){
		p = t[p][s[i]-'a'];
		if(!p) break;
		ans += cnt[p];
	}
	return ans;
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	while(n--){
		scanf("%s",str);
		insert(str);
	}
	while(m--){
		scanf("%s",str);
		printf("%d\n",query(str));
	}
	return 0;
}

POJ3630

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long ll;
const int MAXN = 5e5+7;
string str[MAXN];
int trie[MAXN][17],idx,vis[MAXN];

bool insert(string s)
{
	int p = 0,len = s.size();
	for(int i = 0;i < len;i ++){
		int ch = s[i]-'0';
		if(!trie[p][ch]) trie[p][ch] = ++idx;
		if(vis[p]) return true;
		p = trie[p][ch];
	}
	vis[p] = 1;
	return false;
}

bool cmp(string a,string b)
{
	int lena = a.size(),lenb = b.size();
	return lena < lenb;
}

int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		int n;
		memset(vis,0,sizeof(vis));
		memset(trie,0,sizeof(trie));
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++){
			cin>>str[i];
		}
		sort(str+1,str+1+n,cmp);
		//for(int i = 1;i <= n;i ++) cout<<str[i]<<endl;
		int flag = 0;
		for(int i = 1;i <= n;i ++){
			if(insert(str[i])){
				flag = 1;
				break;
			}
		}
		if(flag)puts("NO");
		else puts("YES");
	}
	return 0;
}

二:trie不尽可以对字符串进行操作,同时也可以操作整数,因为十进制数们可以转化为二进制下的01字符串因此 经常使用trie对整数的异或操作进行解决。
例题:最大异或和
题意:给定n个数,找出他们之间两两组合异或能得到的最大值。
思路:把每个数看做01二进制串,用trie来储存每个整数的01串。进行查询的时候,想要异或和最大只需要,每一位对应的二进数尽可能的不同,因此查询的时候,最好的解决方法就是走向与当前这一位值不相同的方向,如果不同的方向不存在,就走相同的方向。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+7;
const int N = 5e5+7;
int trie[31*MAXN][2],idx;
int a[MAXN];
// 这道题就说明 trie 不仅可以 操作字符串 同时还可以对二进制状态下的01串进行操作
void insert(int x)
{
	int p = 0;
	for(int i = 30;i >= 0;i --){
		int ch = x>>i & 1;
		if(!trie[p][ch])
			trie[p][ch] = ++idx;
		p = trie[p][ch];
	}
}

int search(int x){
	int p = 0,sum = 0,cnt = 0;
	for(int i = 30;i >= 0;i --){
		int ch = x>>i & 1;// 二进制下的取每一位的方法
		if(trie[p][!ch]){
			p = trie[p][!ch];
			sum += (1<<i)*(!ch);
		}
		else if(trie[p][ch]){
			p = trie[p][ch];
			sum += (1<<i)*ch;
		}
		else break;
	}
	return sum;
}

int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++){
		scanf("%d",&a[i]);
		insert(a[i]);
	}
	int ans = 0;

	for(int i = 1;i <= n;i ++){
		ans=max(ans,search(a[i])^a[i]);
		//printf("%d\n",search(a[i]));
	}
	printf("%d\n",ans);
	return 0;
}

HDU5536
这道题用到了trie的删除操作
求给定n个数
求出三个互不相同的数使得(a[i]+a[j])^ a[k] 的值最大
因为这道题要求三个数不能相同因此需要在查询前先把i和j从trie删除,这个可以用一个flag数组标记完成,插入时++,删除时–。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1e3+100;
int a[MAXN];
//因为要保证 三个数不能有相同的 因此这次要用到 trie树的删除结构
int trie[MAXN*31][2],idx,val[MAXN*31];//val数组保存存入的值
int flag[MAXN*31];

void insert(int x)
{
	int p = 0;
	for(int i = 30;i >= 0;i --){
		int u = x>>i & 1;
		if(!trie[p][u]) trie[p][u] = ++idx;
		p = trie[p][u];
		flag[p]++;//和后面的删除对着
	}
	val[p] = x;
}

void del(int x)//删除操作 不同就是 查询之前 先把 si,sj删除 然后再查询 查询完后 再插入si,sj
{
	int p = 0;
	for(int i = 30;i >= 0;i --){
		int u = x>>i & 1;
		p = trie[p][u];
		flag[p]--;
	}
}

int query(int x)
{
	int p = 0;
	for(int i = 30;i >= 0;i --){
		int u = x>>i & 1;
		p = (flag[trie[p][!u]]?trie[p][!u]:trie[p][u]);
	}
	return val[p]^x;
}

int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		memset(flag,0,sizeof(flag));
		memset(val,0,sizeof(val));
		memset(trie,0,sizeof(trie));
		idx = 0;
		int n;
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++){
			scanf("%d",&a[i]);
			insert(a[i]);
		}
		int ans = 0;
		for(int i = 1;i < n;i ++){
			for(int j = i+1;j <= n;j ++){
				del(a[i]);
				del(a[j]);
				ans =  max(ans,query(a[i]+a[j]));
				insert(a[i]);
				insert(a[j]);
			}
		}
		printf("%d\n",ans);
	}	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值