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