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在Trie是否存在时,我们令一个指针p起初指向根节点,然后依次扫描S中的每个字符c:
1.若p的c字符指向空,则说明S没有被插入过Trie,结束检索。
2.若p的c字符指向一个已经存在的节点Q,则令P=Q。
当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,则说明S在Trie中存在,否则说明S没有查如果Trie。
int trie[SIZE][26],tot=1;//初始化,假设字符串由小写字母构成
void insert(char*str)//插入一个字符串
{
int len=strlen(str),p=1;
for(int ik=0;k<len;k++)
{
int ch=str[k]-'a';
if(trie[p][ch]==0)trie[p][ch]=++tot;
p=trie[p][ch];
}
ed[p]=true;
}
bool search(char*str)//检索字符串是否存在
{
int len=strlen(str);
for(int k=0;k<len;k++)
{
int ch=str[k]-'a';
p=trie[p][ch];
if(p==0)return false;
}
return ed[p];
}
例题
模板题,只需要增加一个cnt数组。
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
char s[1000005];
int trie[1000005][26],tot=1;
int cnt[1000005];
void insert(char*s)
{
int len=strlen(s),p=1;
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]+=1;
return ;
}
int search(char*s)
{
int ans=0;
int len=strlen(s),p=1;
for(int i=0;i<len;i++)
{
p=trie[p][s[i]-'a'];
if(p==0)return 0;
ans+cnt[p];
}
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%s",s);
insert(s);
}
for(int i=1;i<=m;i++)
{
scanf("%s",s);
cout<<search(s)<<endl;
}
return 0;
}
把每一个以二进制数的形式插入Trie,在search的过程中,尽可能走和当前位相反的那一个分支,如果走不通再走和当前位相同的分支,最终可以得到最大异或值。
#include<iostream>
using namespace std;
#define MAX_N 100000
int n;
int trie[MAX_N*31][2];
int tot=0;
int num[MAX_N+5];
void insert(int num)
{
int p=0;
for(int i=30;i>=0;i--)
{
int now=num>>i&1;
if(trie[p][now]==0)trie[p][now]=++tot;
p=trie[p][now];
}
return ;
}
int search(int num)
{
int p=0,ans=0;
for(int i=30;i>=0;i--)
{
int now=num>>i&1;
if(trie[p][1^now])
{
p=trie[p][1^now];
ans+=1<<i;
}
else p=trie[p][now];
}
return ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>num[i];
insert(num[i]);
}
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,search(num[i]));
cout<<ans<<endl;
return 0;
}
两个点之间的异或和等价于从根节点到点a的异或和与从根节点到点b的异或和进行一次异或。
进行一遍深搜,找到从根节点到达所有节点的路径上的异或和。
之后可以将本题完全转化为上一题。
#include<iostream>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
#define MAX_N 100000
vector<PII>ed[MAX_N+5];
int val[MAX_N+5];
int trie[MAX_N*31][2];
int tot=0;
int n;
void dfs(int now,int fa,int sum)
{
val[now]=sum;
for(auto k:ed[now])
if(k.first!=fa)
dfs(k.first,now,sum^k.second);
return ;
}
void insert(int num)
{
int p=0;
for(int i=30;i>=0;i--)
{
int now=num>>i&1;
if(trie[p][now]==0)trie[p][now]=++tot;
p=trie[p][now];
}
return ;
}
int search(int num)
{
int p=0,ans=0;
for(int i=30;i>=0;i--)
{
int now=num>>i&1;
if(trie[p][1^now])
{
p=trie[p][1^now];
ans+=1<<i;
}
else p=trie[p][now];
}
return ans;
}
int main()
{
cin>>n;
for(int i=1,u,v,w;i<=n-1;i++)
{
cin>>u>>v>>w;
ed[u].push_back({v,w});
ed[v].push_back({u,w});
}
dfs(0,-1,0);
for(int i=0;i<n;i++)
insert(val[i]);
int ans=0;
for(int i=0;i<n;i++)
ans=max(ans,search(val[i]));
cout<<ans;
return 0;
}