【数据结构】 字典树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;
}
posted @ 2024-12-03 20:23  ZYStream  阅读(46)  评论(0编辑  收藏  举报