「Tire树」学习笔记

定义与基本求法

  • 定义

    又称字典树,用边表示字母,从根节点到树上某一节点路径形成一个字符串。

    例如 charlie:

    image

  • 基本求法

    廷显然的,往树中存就行了,查询也是显然的,通过一道例题来理解吧:

    #include<bits/stdc++.h>
    #define int long long
    #define endl '\n'
    using namespace std;
    const int N=5e5+10,P=1e9+7;
    template<typename Tp> inline void read(Tp&x)
    {
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
    }
    char s[N];
    int n,m,t[N][30],v[N],tot=1;
    void Tire(char s[])
    {
    int r=1,l=strlen(s+1);
    for(int i=1;i<=l;i++)
    {
    int c=s[i]-'a';
    if(!t[r][c]) t[r][c]=++tot;
    r=t[r][c];
    }
    v[r]=1;
    }
    void ask(char s[])
    {
    int r=1,l=strlen(s+1);
    for(int i=1;i<=l;i++)
    {
    int c=s[i]-'a';
    r=t[r][c];
    if(!r) break;
    }
    if(v[r]==1)
    {
    cout<<"OK"<<endl;
    v[r]=2;
    }
    else if(v[r]==2)
    cout<<"REPEAT"<<endl;
    else cout<<"WRONG"<<endl;
    }
    signed main()
    {
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n);
    for(int i=1;i<=n;i++)
    cin>>s+1,
    Tire(s);
    read(m);
    for(int i=1;i<=m;i++)
    cin>>s+1,
    ask(s);
    }

    t 数组存的现节点是编号,第一维存的是根节点编号,第二维是边权。

    查询时,遇到该字符对应编号为 0 ,说明这个字符串不存在与字典树中。

例题

  • Phone List

  • 题面:

    给定 n 个字符串,判断是否存在两个字符串 s,t ,使 st 的前缀。

  • 解法:

    多测,n2 匹配—— Hash×

    将每一组存到字典树中,同时查询是否有前缀等于之前的串即可。

    可以定义一个新的数组 fp 用于判断,判断字符串 s 时,只要出现 fp=1 就说明有字符串与其匹配了,当然,存的时候,存完另 fp=1

    结合代码理解。

  • 代码如下:

    #include<bits/stdc++.h>
    #define int unsigned long long
    #define endl '\n'
    using namespace std;
    const int N=1e6+10,P=1e9+7;
    template<typename Tp> inline void read(Tp&x)
    {
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
    }
    int T,n,t[N][20],tot=1;
    char s[N];
    bool f[N];
    void Tire(char s[])
    {
    int p=1,l=strlen(s+1);
    for(int i=1;i<=l;i++)
    {
    int c=s[i]-'0';
    if(!t[p][c]) t[p][c]=++tot;
    p=t[p][c];
    }
    f[p]=1;
    }
    bool find(char s[])
    {
    int p=1,l=strlen(s+1);
    for(int i=1;i<=l;i++)
    {
    int c=s[i]-'0';
    if(!t[p][c]) return 0;
    p=t[p][c];
    if(f[p]) return 1;
    }
    return 1;
    }
    signed main()
    {
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(T);
    while(T--)
    {
    memset(f,0,sizeof(f));
    memset(t,0,sizeof(t));
    tot=1;
    bool ans=0;
    read(n);
    for(int i=1;i<=n;i++)
    {
    cin>>s+1;
    if(find(s)) ans=1;
    Tire(s);
    }
    if(!ans) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
    }
    }

01Tire

定义与基本求法:

  • 定义

    字符集只有 0||1Tire 数,主要用来解决有关异或值的问题。

  • 基本求法:

    异或有着安慰考虑的性质,每一位贡献是分开的,这与 Tire 树用不同深度存不同位定性质是吻合的。

    如果要最大化异或值,一定先最大化其最高位,如果用 Tire 树从高到低来做,正好吻合了这个贪心思想。

    根据例题来理解吧。

例题

TheXORLargestPair

  • 题目链接

  • 题面:

    给定 n 个整数 aian ,在其中选出两个进行异或运算,求可以得到的最大结果。

  • 解法:

    n 足够大,暴力不要想。

    将这 n 个数转换成二进制,存到 Tire 树里。

    再取这 n 个树,在 Tire 上跑一边,尽可能的向与其二进制位不同的方向。

    此处体现了贪心的思想,因为二进制下 1aaaa>0bbbb 始终成立,高位优一定全局优。

  • 代码如下:

    #include<bits/stdc++.h>
    #define int long long
    #define endl '\n'
    using namespace std;
    const int N=3e6+10,P=1e9+7;
    template<typename Tp> inline void read(Tp&x)
    {
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
    }
    int n,a[N],tot,t[N][2],ans;
    void Tire(int x)
    {
    int p=0;
    for(int i=31;i>=0;i--)
    {
    int l=(x>>i)&1;
    if(!t[p][l]) t[p][l]=++tot;
    p=t[p][l];
    }
    }
    int find(int x)
    {
    int p=0,sum=0;
    for(int i=31;i>=0;i--)
    {
    int l=(x>>i)&1;
    if(t[p][l^1])
    p=t[p][l^1],
    sum=sum<<1|1;//二进制运算
    else
    p=t[p][l],sum=sum<<1;//同上
    }
    return sum;
    }
    signed main()
    {
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n);
    for(int i=1;i<=n;i++)
    read(a[i]),
    Tire(a[i]);
    for(int i=1;i<=n;i++)
    ans=max(ans,find(a[i]));
    cout<<ans;
    }

    位运算方向注意不要写反了。

TheXORlongestPath

  • 题目链接

  • 题面:

    给定一棵 n 个节点的带权树,求书上最长的异或和路径。

  • 解法:

    首先了解异或的一个重要性质——自反性:

    xx=0

    所以一个元素,若对其进行了重复偶数次的重复,则视作没有异或。

    path(x,y)=path(x,lca)path(lca,y)=path(x,root)path(root,y)

    故此求出每个点到根节点的异或和 di ,将 di 存到 Tire 中,问题就转化为了上一道题。

    至于如何求每个节点到根节点的异或和,可以用 dfs 解决。

总结

对于 Tire 树此处涉及并不多,也还没有讲,上网上自己找的。

直接使用 Tire 树的问题还是相对容易的,也很好理解,01Tire 也是直接使用 Tire 的一个应用了,虽然只能解决异或问题。

而此处对其进行整理主要为了为后面的 AC 自动机等知识点做准备。

由此看,Tire 树还是很重要的,要牢牢掌握。

posted @   卡布叻_周深  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示