数据结构基础【Trie(字典树)】

Trie

  • 定义:

        Trie (字典树)是一种用于实现字符串快速检索的多叉树结构。Trie的每个节点都拥有若于个字符指针,若在插入或检索字符串时扫描到一个字符c, 就沿着当前节点的C字符指针,走向该指针指向的节点。

  • 初始化

        一棵空Trie仅包含一个根节点,该点的字符指针均指向空。

  • 插入操作

       当需要插入一个字符串S时,我们令一个指针P起初指向根节点。然后,依次扫描S中的每个字符c。

  1. 若P的c字符指针指向一个已经存在的节点Q,则令P= Q.
  2. 若P的c字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q,然后令P=Q.

       当s中的字符扫描完毕时,在当前节点P上标记它是一个字符串的末尾。

  • 检索操作

       当需要检索-个字符串S在Thie中是否存在时,我们令一个指针P起初指向根节点,然后依次扫描s中的每个字符c:

  1. 若P的c字符指针指向空,则说明s没有被插入过Trie, 结束检索。
  2. 若P的C字符指针指向一个已经存在的节点Q,则令p=Q。

       当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,则说明s在Tie中存在,否则说明S没有被插入过Tie.

在这里插入图片描述
       可以看出在Trie中,字符数据都体现在树的边(指针)灰色标记了单词的末一些额外信息, 例如单词结尾标记等。

  • 插入代码
int trie[size][26], tot = 1;
bool end[size];
void insert(char *s){
	int p = 1;
	int len = strlen(s);
	for(int i = 0; i < len; i++){
		int ch =s[i] - 'a';
		if(trie[p][ch] == 0) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	end[p] = true; //表示这里存了一个单词
}
  • 检索代码
bool search(char *s){
	int len = strlen(s), p = 1;
	for(int i = 0; i < len; i++){
		p = trie[p][s[i] - 'a'];
		if(p == 0) return false; 
	}
	return end[p]; 
}

题目大意:
       给定N个字符串S1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。输入字符串的总长度不超过106,仅包含小写字母。
思路:
       把这N个字符串插入一棵 Trie树,Trie 树的每个节点上存储一个整数cnt. 记录该节点是多少个字符串的末尾节点。(为了处理插入重复字符串的情况,这里要记录个数,而不能只做结尾标记)
        对于每个询问,在Trie树中检索T, 在检索过程中累加途径的每个节点的cnt值,就是该询问的答案。
代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
const int N = 1e6 + 10;
int n, m;
int trie[N][26], cnt[N], tot = 1;
char s[N];
void insert(){
	int p = 1;
	int len = strlen(s);
	for(int i = 0; i < len; i++){
		int ch =s[i] - 'a';
		if(trie[p][ch] == 0) trie[p][ch] = ++tot;
		p = trie[p][ch];
	}
	cnt[p]++;
}
int query(){
	int p = 1;
	int res = 0;
	int len = strlen(s);
	for(int i = 0; i < len; i++){
		int x = trie[p][s[i] - 'a'];
		if(!x) break;
		p = x;
		res += cnt[p];
	}
	return res;
}
int main(){
	scanf("%d%d", &n, &m);
	while(n--){
		scanf("%s", s);
		insert();
	}
	while(m--){
		scanf("%s", s);
		printf("%d\n", query());
	}
	return 0;
}
  • 例题CH1602 The XOR Largest Pair

  • 题目大意

            在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?

  • 思路

           我们可以把每个整数看作长度为32的二进制01串(数值较小时在前边补0),并且把A1~Aq-1对应的32位二进制串插入一棵Trie 树(其中最低二进制位为叶子节点)。接下来,对于A;对应的32位二进制串,我们在Trie中进行一次与检索类似的过程,每一步都尝试沿着“与A;的当前位相反的字符指针”向下访问。

在这里插入图片描述
遍历树,尽量走相反的字符指针,最后得到的即是最大值。

  • 代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
const int N = 1e5 + 10, M = 3000000;
int n, m;
int a[N];
int trie[M][2], tot = 1;
void insert(int x){
	int p = 1;
	for(int i = 31; i >= 0; i--){
		if(!trie[p][x >> i & 1]) trie[p][x >> i & 1] = ++tot;
		p = trie[p][x >> i & 1]; 
	}
}
int query(int x){
	int ans = 0;
	int p = 1;
	for(int i = 31; i >= 0; i--){
		int y = x >> i & 1;
		if(trie[p][!y]){
			ans += 1 << i;
			p = trie[p][!y];
		}
		else p = trie[p][y];
	}
	return ans;
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		insert(a[i]);
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		ans = max(ans, query(a[i]));
	}
	cout << ans << endl;
	return 0;
}
发布了54 篇原创文章 · 获赞 155 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_45432665/article/details/104061994