【数据结构】 字典树trie详解
定义:
将多个字符串以树的方式存储即为字典树,如图,\(1,4,3,12\) 表示 \(cca\) ,我么用 \(ch[i][j]\) 来表示第 \(i\) 个节点的 \(j\) 字符所指向的下一个节点,\(tag[u]\) 表示节点 \(u\) 是否代表一个字符串的结尾,如果是的话,\(tag[u]=1\)。
模板CODE
添加字符串
//n表示即将要向字典树插入n个字符串
const N 100005;
scanf("%d",&n);
for (int i=1;i<=n;i++){
char s[N];
scanf("%s",s+1);
int u=1;
for (int j=1;s[j];j++){
int c=s[j]-'a';
if (!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
}
tag[u]=1;
}
字符串查找
//查看字符串s是否在字典树当中,如果在输出OK,否则输出WRONG
char s[N];
scanf("%s",s+1);
int u=1;
for (int i=1;s[i];i++){
int c=s[i]-'a';
u=ch[u][c];
if (!u) break;
}
if (tag[u]==1){
puts("OK");
continue;
}
else {
puts("WRONG");
continue;
}
例题
P2580 于是他错误的点名开始了
例题代码:
点击查看代码
#include<cstdio>
using namespace std;
const int N=500010;
int n,m;
int ch[N][30];
int tag[N];
int tot=0;
char s[N];
int main(){
freopen("test_point/input.in","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%s",s+1);
int u=1;
for (int j=1;s[j];j++){
int c=s[j]-'a';
if (!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
}
tag[u]=1;
}
scanf("%d",&m);
while (m--){
scanf("%s",s+1);
int u=1;
for (int i=1;s[i];i++){
int c=s[i]-'a';
u=ch[u][c];
if (!u) break;
}
if (tag[u]==2){
puts("REPEAT");
continue;
}
else if (tag[u]==1){
puts("OK");
tag[u]=2;
continue;
}
else {
puts("WRONG");
continue;
}
}
return 0;
}
维护异或极值
01-trie:01-trie 是指字符集为 {0,1} 的 trie。01-trie 可以用来维护一些数字的异或和,支持修改(删除 + 重新插入),和全局加一
维护异或路径:此类问题会给定一棵树,让你求出这棵树最长的异或路径,对于此类问题,我们需要先明白异或运算的性质
模板题目: https://www.luogu.com.cn/problem/P4551
- 交换律: \(a \oplus b=b \oplus a\)
- 结合律:\((a \oplus b) \oplus c=a \oplus (b \oplus c)\)
- 自反性:\(a \oplus a=0\)
- 与 \(0\) 异或:\(a \oplus 0=a\)
根据以上性质我们可以得到一个推论:\(a \oplus b \oplus b=a \oplus (b \oplus b)=a \oplus 0=a\) ,因此,在树或图中,如果定义一条路径的异或值为路径上所有边权的异或和,那么:
从节点 \(u\) 到节点 \(v\) 的路径异或值等于从根节点到 \(u\) 的异或值 \(xor[u]\) 与从根节点到 \(v\) 的异或值 \(xor[v]\) 的异或,因此我们可以预处理所有节点到根节点的异或路径,存在 \(xor[]\) 数组中,然后对 \(xor[]\) 数组构造字典树,如下图
$\longrightarrow $
接着,我们存好后,用两个指针在字典树上遍历,对于每次,对要尽可能选择不一样的两位,这样可以保证异或值最大,因为高位的 \(1\) 比他下面所有位的 \(1\) 加起来都大,但是如果存在两种选择都可以让该为异或为 \(1\) ,则需要dfs一下
模板CODE:
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N=100005;
int n;
int tr[2000000][2];
struct edge{
int v;
int w;
};
vector<edge> g[N];
int xr[N];
int cnt=1;
int h;
int res,ans;
void dfs(int u,int fa){
for (edge ed:g[u]){
int v=ed.v,w=ed.w;
if (v==fa) continue;
xr[v]=xr[u]^w;
dfs(v,u);
}
return ;
}
void dfs1(int i,int j,int k){
if (!k){
ans=max(ans,res);
return ;
}
if ((tr[i][0]&&tr[j][1])||(tr[i][1]&&tr[j][0])){
if (tr[i][0]&&tr[j][1]){
res+=(1<<(k-1));
dfs1(tr[i][0],tr[j][1],k-1);
res-=(1<<(k-1));
}
if (tr[i][1]&&tr[j][0]){
res+=(1<<(k-1));
dfs1(tr[i][1],tr[j][0],k-1);
res-=(1<<(k-1));
}
}
else{
if (tr[i][0]&&tr[j][0]) dfs1(tr[i][0],tr[j][0],k-1);
else if (tr[i][1]&&tr[j][1]) dfs1(tr[i][1],tr[j][1],k-1);
}
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n-1;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].push_back((edge){v,w});
g[v].push_back((edge){u,w});
}
dfs(1,-1);
int maxn=0;
for (int i=1;i<=n;i++) maxn=max(maxn,xr[i]);
while (maxn) maxn>>=1,h++;
if (h==0) h++;
for (int i=1;i<=n;i++){
int u=1;
for (int j=h-1;j>=0;j--){
int w=(xr[i]>>j)&1;
if (!tr[u][w]) tr[u][w]=++cnt;
u=tr[u][w];
}
}
dfs1(1,1,h);
printf("%d",ans);
return 0;
}