字典树Trie

应用一:单词匹配

给定 s 1 , s 2 , . . . , s n s_1,s_2,...,s_n s1,s2,...,sn 字符串,有若干次查询,对于每次查询,输入一个字符串 T T T,问是否存在于 s s s 中(要完全对应于其中一个 s s s 才算存在)。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;

bool tail[maxn];   //尾部标记
int trie[maxn][26], tot;

void insert(string str){
	int p = 1;
	for(int i = 0; i < str.length(); i++){
		int ch = str[i] - 'a';
		if(trie[p][ch] == 0) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	tail[p] = true;   
}
bool search(string str){
	int p = 1;
	for(int i = 0; i < str.length(); i++){
		p = trie[p][str[i] - 'a'];
		if(p == 0) return false;
	}
	return tail[p];
}
inline void init(){
	memset(tail,false,sizeof tail);
	tot = 1;
}
int main()
{
	init();
	int n;
	cin>>n;
	string s;
	for(int i=0;i<n;i++){
		cin>>s;
		insert(s);
	}
	for(int i=0;i<n;i++){
		cin>>s;
		cout<<search(s)<<endl;
	}
	return 0;
}

应用二:前缀匹配

给定 s 1 , s 2 , . . . , s n s_1,s_2,...,s_n s1,s2,...,sn 字符串,有若干次查询,对于每次查询,输入一个字符串 T T T,问 T T T 是否对应其中一个 s s s 的前缀(完全对应可行)。
p s ps ps:相对于应用一,去掉尾部标记。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;

int trie[maxn][26], tot;

void insert(string str){
	int p = 1;
	for(int i = 0; i < str.length(); i++){
		int ch = str[i] - 'a';
		if(trie[p][ch] == 0) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
}
bool search(string str){
	int p = 1;
	for(int i = 0; i < str.length(); i++){
		p = trie[p][str[i] - 'a'];
		if(p == 0) return false;
	}
	return true;
}
inline void init(){
	tot = 1;
}
int main()
{
	init();
	int n;
	cin>>n;
	string s;
	for(int i=0;i<n;i++){
		cin>>s;
		insert(s);
	}
	for(int i=0;i<n;i++){
		cin>>s;
		cout<<search(s)<<endl;
	}
	return 0;
}

应用三:单词统计

给定 N N N 个字符串 S 1 , S 2 . . . S N S_1,S_2...S_N S1,S2...SN,接下来进行M次询问,每次询问给定一个字符串 T T T,求 S 1 ~ S N S_1~S_N S1SN 中有多少个字符串是 T T T 的前缀。
题目来源:CH1601

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;

int n,m;
int trie[maxn][26],tot;
ll tail[maxn];

void insert(string str){
	int p = 1;
	for(int i = 0; i < str.length(); i ++){
		int ch = str[i] - 'a';
		if(trie[p][ch] == 0) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	tail[p]++;
}
ll search(string str){
	int p = 1;
	ll sum = 0;
	for(int i = 0; i < str.length(); i++){
		p = trie[p][str[i] - 'a'];
		if(p == 0) break;
		sum += tail[p];
	}
	return sum;
}
inline void init(){
	tot = 1;
	memset(trie, 0, sizeof trie);
	memset(tail, 0, sizeof tail);
}
int main()
{
	cin>>n>>m;
	string s,t;
	init();
	for(int i=0;i<n;i++){
		cin>>s;
		insert(s);
	}
	for(int i=0;i<m;i++){
		cin>>t;
		cout<<search(t)<<endl;
	}
	return 0;
}

应用四:前缀统计

老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
题目来源:poj1251

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 1e6 + 10;

int trie[maxn][26], tot;
char s[maxn];
int tail[maxn];

void init(){
	tot = 1;
	memset(tail,0,sizeof tail);
}

void insert(char* s){
	int p = 1,len = strlen(s);
	for(int i = 0;i < len; i++){
		int ch = s[i] - 'a';
		if(!trie[p][ch]) trie[p][ch] = ++tot;
		p = trie[p][ch];
		tail[p]++;
	}
}
int search(char* s){
	int p = 1, len = strlen(s);
	for(int i = 0; i < len; i++){
		p = trie[p][s[i]-'a'];
		if(p == 0) return 0;
	} 
	return tail[p];

}
int main()
{
	init();
	while(gets(s),s[0]){
		insert(s);
	}

	while(gets(s)){
		cout<<search(s)<<endl;
	}
	return 0;
}

应用五:Phone List

题目描述:输出 n n n 个电话号码,若其中有两个号码存在谁是谁的前缀,则输出NO,否则输出YES
题目来源poj3630

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 10000 + 10;

int n;
int trie[maxn * 10 + 5][10], tot;
bool tail[maxn * 10 + 5];

void init(){
	tot = 1;
	memset(tail, false, sizeof tail);
	memset(trie,0,sizeof trie);
}

void insert(string s){
	int p = 1;
	for(int i = 0; i < s.length(); i++){
		int ch = s[i] - '0';
		if(!trie[p][ch]) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	tail[p] = true;
}
int search(string s){
	int p = 1;
	for(int i = 0; i < s.length(); i++){
		p = trie[p][s[i]-'0'];
		if(p == 0) return false;
		if(tail[p] == true) return true;
	}
	return true;
}

int main()
{
	int t,flag;
	string s;
	cin>>t;
	while(t--){
		cin>>n;
		flag = 0;
		init();
		for(int i = 0; i < n; i++){
			cin>>s;
			if(!flag){    //如果已经确定是NO,就不用再查了
				if(search(s) == true){
					flag = 1;
				}
				insert(s);
			}
		}
		if(flag == 1) cout<<"NO"<<endl;
		else cout<<"YES"<<endl;
	}
	return 0;
}

应用六:最大XOR数对

题目描述:在给定的 N N N 个整数 A 1 , A 2 … … A N A_1,A_2……A_N A1A2AN中选出两个进行xor运算,得到的结果最大是多少?
解题思路:将每位 A i A_i Ai 的二进制存入字典树中。然后对每一位 A i A_i Ai,在字典树中找。
题目来源:CH1602

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 2e6 + 10;

int n;
int trie[maxn][2], tot;
int tail[maxn];

void insert(int x){
	int p = 1;
	for(int i = 30; i >= 0; i--){
		int ch = (x>>i)&1;
		if(!trie[p][ch]) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	tail[p] = x;
}
int search(int x){
	int p = 1;
	for(int i = 30; i >= 0; i--){
		int ch = (x>>i)&1;
		if(trie[p][ch^1])
			p = trie[p][ch^1];
		else p = trie[p][ch];
	}
	return tail[p]^x;
}
void init(){
	tot = 1;
}
int main()
{
	cin>>n;
	init();
	int a,res=0;
	for(int i = 1; i <= n; i++){
		cin>>a;
		res = max(res, search(a));  //每次在1~i-1中找到最大xor
		insert(a);
	}
	cout <<res<<endl;
	return 0;
}

应用七:The xor-longest Path

题目描述:一棵有 n n n 个节点的树,每条边都有一个权值。在树上找两个节点 x x x y y y,使得这条路径上的值的异或和最大,输出异或和的最大值。
解题思路:用 d [ x ] d[x] d[x] 保存 x x x 节点到根节点路径上所有数的异或和,然后问题转化成从 d d d 数组中选区两个数,使得这两个数的异或和最大。因为 x x x 节点和 y y y 节点的 L C A LCA LCA 到根节点的路径上的异或和抵消了。
题目来源poj3764
一开始 t a i l tail tail 数组开小了导致总是没有过,也没有找出问题,谢谢一个在校同伴救了我

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
struct edge{
	int to, w, next;
};
const int maxn = 1e5 + 100;

int n;
int trie[maxn*31][2], tot;
int head[maxn], cnt;
int tail[maxn*31];
edge e[maxn*2];
int d[maxn];

void init(){
	cnt = 0;
	tot = 1;
	memset(head, -1, sizeof head);
	memset(trie, 0, sizeof trie);
	memset(d, 0, sizeof d);
	memset(tail,0,sizeof tail);
}
void addedge(int u, int v, int w){
	e[cnt].to = v; e[cnt].w = w;
	e[cnt].next = head[u]; head[u] = cnt++;
}
void dfs(int u, int fa, int val){
	d[u] = val;
	for(int i = head[u]; ~i; i = e[i].next){
		if(e[i].to != fa)
			dfs(e[i].to, u, e[i].w ^ val);
	}
}
void insert(int x){
	int p = 1;
	for(int i = 30; i >= 0; i--){
		int ch = (x>>i)&1;
		if(!trie[p][ch]) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	tail[p] = x;
}
int search(int x){
	int p = 1;
	for(int i = 30; i >= 0; i--){
		int ch = (x>>i)&1;
		if(trie[p][ch^1])
			p = trie[p][ch^1];
		else p = trie[p][ch];
	}
	return tail[p] ^ x;
}
int main()
{
	int u, v, w;
	while(cin>>n){
		init();
		for(int i = 1; i < n; i++){
			scanf("%d%d%d",&u,&v,&w);
			addedge(u, v, w);
			addedge(v, u, w);
		}
		dfs(0, 0, 0);
		int res = 0;
		for (int i = 0; i < n; ++i)
		{
			res = max(res, search(d[i]));
			insert(d[i]);
		}
		cout<<res<<endl;
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值