• 【字符串】Trie树


    trie树

    学习资料:OI Wiki

    模板:

    struct Tire{
        int nxt[maxn][26],cnt;
        bool exit[maxn];//以这个点结尾的字符串是否存在
        void insert(char s[],int len)
        {
            int p=0;
            for(int i=0;i<len;i++){
                int c=s[i]-'a';
                if(!nxt[p][c])nxt[p][c]=++cnt;
                p=nxt[p][c];
            }
            exit[p]=1;
        }
        bool find(char s[],int len){
            int p=0;
            for(int i=0;i<len;i++){
                int c=s[i]-'a';
                if(!nxt[p][c])return false;
                p=nxt[p][c];
            }
            return exit[p];
        }
    }tr;
    

    例题和作用:

    Trie中的节点表示的是某个模式串的前缀,也称状态。Tire的边就是状态的转移。

    1,检索字符串,查找一个字符串是否出现过

    例题1:luoguP2580

    题意:

    首先给一个整数 (n)(n) 个字符串,之后一个整数 (m)(m) 个字符串,要求对这 (m) 个字符串输出 (m) 行:

    • 如果该字符串不存在于 (n) 个给定字符串中,则输出 "WRONG";
    • 否则,如果该字符串第一次出现在 (m) 个字符串中,则输出 “OK”;
    • 否则,输出 "REPEAT"。

    解:

    hash可解,tire也可解。此处用tire

    #include<bits/stdc++.h>
    #define reg register
    using namespace std;
    typedef long long ll;
    const int maxn=5e5+5;
    
    struct Tire{
        int nxt[maxn][27],cnt;
        bool exit[maxn],vis[maxn];
        void insert(char s[],int len)
        {
            int p=0;
            for(int i=0;i<len;i++){
                int c=s[i]-'a';
                if(!nxt[p][c])nxt[p][c]=++cnt;
                p=nxt[p][c];
            }
            exit[p]=1;
        }
        int find(char s[],int len){
            int p=0;
            for(int i=0;i<len;i++){
                int c=s[i]-'a';
                if(!nxt[p][c])return 0;
                p=nxt[p][c];
            }
            return exit[p]?(vis[p]==1?-1:(vis[p]=1)):0;
        }
    }tr;
    int main()
    {
        int n,m;char s[55];
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            tr.insert(s,strlen(s));
        }
        scanf("%d",&m);
        while(m--)
        {
            scanf("%s",s);
            int res=tr.find(s,strlen(s));
            if(!res)puts("WRONG");
            else if(res==1)puts("OK");
            else puts("REPEAT");
        }
    }
    

    2,维护异或和

    例题2:luoguP6018

    题意:给定一棵 (n) 个节点的无向无根树,每个节点有权值 (a_i) ,之后 (m) 个操作,操作有三种:

    • 1 x :将与节点 (x) 距离为 (1) 的节点(即,与 (x) 直接相连的节点)的权值增 (1)
    • 2 x v:将节点 (x) 的权值减 (v) .
    • 3 x :询问与节点 (x) 距离为 (1) 的节点的权值异或和。

    对每个询问输出答案。保证任意时刻每个节点的权值非负。(nleq5 imes10^5,mleq5 imes10^5,0leq a_ileq10^5,1leq xleq n)

    解:

    对于 (1) 操作,每个点建一棵 01tire ,支持全局加1和维护异或和。

    对于 (2) 操作,(mathcal{O(1)}) 维护修改。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=5e5+5;
    const int MAXN=2e7+5;
    const int MA=23;
    
    int nxt[MAXN][2],cnt,num[MAXN],dep[MAXN];
    struct Tire{
        int dval[MA];
        void insert(int &rt,int x,int d)
        {
            if(d>=MA)return;
            if(!rt)rt=++cnt;
            if(x&1){
                insert(nxt[rt][1],x>>1,d+1);
                if(nxt[rt][1])num[nxt[rt][1]]++,dval[d]++;
            }else{
                insert(nxt[rt][0],x>>1,d+1);
                if(nxt[rt][0])num[nxt[rt][0]]++;
            }
            dep[rt]=d;
        }
        void erase(int rt,int x,int d)
        {
            if(d>=MA)return;
            if(x&1){
                insert(nxt[rt][1],x>>1,d+1);
                if(nxt[rt][1])num[nxt[rt][1]]--,dval[d]--;
            }else{
                insert(nxt[rt][0],x>>1,d+1);
                if(nxt[rt][0])num[nxt[rt][0]]--;
            }
        }
        void addone(int rt)
        {
            dval[dep[rt]]-=num[nxt[rt][1]];
            swap(nxt[rt][1],nxt[rt][0]);
            dval[dep[rt]]+=num[nxt[rt][1]];
            if(nxt[rt][0])addone(nxt[rt][0]);
        }
        int getval(){
            int res=0;
            for(int i=0;i<MA;i++)res+=(dval[i]&1)<<i;
            return res;
        }
    }tr[maxn];
    int root[maxn];
    int head[maxn],nxtt[maxn<<1],to[maxn<<1],pcnt;
    inline void add(int u,int v){
        to[++pcnt]=v;nxtt[pcnt]=head[u];head[u]=pcnt;
    }
    int fa[maxn];
    void dfs(int u,int f){
        fa[u]=f;
        for(int i=head[u];i;i=nxtt[i])
            if(to[i]!=f)dfs(to[i],u);
    }
    int a[maxn];
    int ad[maxn],dv[maxn];
    void update1(int x){
        if(!fa[x])return;
        int tmp=a[x]+ad[fa[x]]+dv[x];
        tr[fa[x]].erase(root[fa[x]],tmp,0);
        tr[fa[x]].insert(root[fa[x]],tmp+1,0);
    }
    void update2(int x,int v)
    {
        if(!fa[x])return;
        int tmp=a[x]+ad[fa[x]]+dv[x];
        tr[fa[x]].erase(root[fa[x]],tmp,0);
        tr[fa[x]].insert(root[fa[x]],tmp-v,0);
    }
    int main()
    {
        int n,m,u,v;
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        dfs(1,0);
        for(u=1;u<=n;u++)
            for(int i=head[u];i;i=nxtt[i])
                if(to[i]!=fa[u])tr[u].insert(root[u],a[to[i]],0);
        int op;
        while(m--)
        {
            scanf("%d%d",&op,&u);
            if(op==1){
                ad[u]++;tr[u].addone(root[u]);
                if(fa[u])update1(fa[u]);dv[fa[u]]++;
            }else if(op==2){
                scanf("%d",&v);update2(u,v);dv[u]-=v;
            }else{
                printf("%d
    ",(a[fa[u]]+dv[fa[u]]+ad[fa[fa[u]]])^tr[u].getval());
            }
        }
    }
    
    
  • 相关阅读:
    为什么这年头蓝牙功能越来越差
    猜数字-暴力枚举
    怎么使用PHPMailer实现邮件的发送??
    实现windows操作系统和VB下Linux虚拟操作系统相互传取文件方式总结
    第一篇 对Javascript中原型的深入理解
    每天进步一点点——关于SSD写入放大问题
    两步改动CentOS主机名称
    [CentOs7]搭建ftp服务器
    Another app is currently holding the yum lock
    [CentOs7]安装mysql(2)
  • 原文地址:https://www.cnblogs.com/kkkek/p/13796585.html
Copyright © 2020-2023  润新知