Trie
- 定义:
Trie (字典树)是一种用于实现字符串快速检索的多叉树结构。Trie的每个节点都拥有若于个字符指针,若在插入或检索字符串时扫描到一个字符c, 就沿着当前节点的C字符指针,走向该指针指向的节点。
- 初始化
一棵空Trie仅包含一个根节点,该点的字符指针均指向空。
- 插入操作
当需要插入一个字符串S时,我们令一个指针P起初指向根节点。然后,依次扫描S中的每个字符c。
- 若P的c字符指针指向一个已经存在的节点Q,则令P= Q.
- 若P的c字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q,然后令P=Q.
当s中的字符扫描完毕时,在当前节点P上标记它是一个字符串的末尾。
- 检索操作
当需要检索-个字符串S在Thie中是否存在时,我们令一个指针P起初指向根节点,然后依次扫描s中的每个字符c:
- 若P的c字符指针指向空,则说明s没有被插入过Trie, 结束检索。
- 若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];
}
- 标准模板例题: CH1601 前缀统计
题目大意:
给定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;
}
-
题目大意
在给定的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;
}