定义与基本求法

  • 定义

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

    例如 \(charlie:\)

  • 基本求法

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

    #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\) ,使 \(s\) 是 \(t\) 的前缀。

  • 解法:

    多测,\(n^2\) 匹配—— \(Hash×\)

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

    可以定义一个新的数组 \(f_p\) 用于判断,判断字符串 \(s\) 时,只要出现 \(f_p=1\) 就说明有字符串与其匹配了,当然,存的时候,存完另 \(f_p=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||1\) 的 \(Tire\) 数,主要用来解决有关异或值的问题。

  • 基本求法:

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

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

    根据例题来理解吧。

例题

\(The XOR Largest Pair\)

  • 题目链接

  • 题面:

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

  • 解法:

    \(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;
    }

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

\(The XOR-longest Path\)

  • 题目链接

  • 题面:

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

  • 解法:

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

    \(x\oplus x=0\)

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

    ∴ \(path(x,y)=path(x,lca)\oplus path(lca,y)=path(x,root)\oplus path(root,y)\)

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

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

总结

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

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

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

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

Tire树 学习笔记的更多相关文章

  1. zkw线段树学习笔记

    zkw线段树学习笔记 今天模拟赛线段树被卡常了,由于我自带常数 \(buff\),所以学了下zkw线段树. 平常的线段树无论是修改还是查询,都是从根开始递归找到区间的,而zkw线段树直接从叶子结点开始 ...

  2. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  3. 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解

    什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...

  4. Treap-平衡树学习笔记

    平衡树-Treap学习笔记 最近刚学了Treap 发现这种数据结构真的是--妙啊妙啊~~ 咳咳.... 所以发一发博客,也是为了加深蒟蒻自己的理解 顺便帮助一下各位小伙伴们 切入正题 Treap的结构 ...

  5. JSOI2008 Blue Mary开公司 | 李超线段树学习笔记

    题目链接:戳我 这相当于是一个李超线段树的模板qwqwq,题解就不多说了. 代码如下: #include<iostream> #include<cstdio> #include ...

  6. Splay伸展树学习笔记

    Splay伸展树 有篇Splay入门必看文章 —— CSDN链接 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 创造者:Daniel Sleator 和 Robert Ta ...

  7. CART分类与回归树 学习笔记

    CART:Classification and regression tree,分类与回归树.(是二叉树) CART是决策树的一种,主要由特征选择,树的生成和剪枝三部分组成.它主要用来处理分类和回归问 ...

  8. B和B+树学习笔记

    二叉树 如果数据都在内存中,我们就用平衡二叉查找树即可,这样效率最高. 在前面的文章中我使用过红黑树(大致平衡的二叉查找树),500万节点时,搜索的深度可以达到50,也就是需要50次指针操作才能获取到 ...

  9. Trie树 字典树-学习笔记

    字符串--蒟蒻永远的阴影 对于字符串匹配 KMP很好的解决了以一个文本串匹配一个模板串的问题 但如果模板串有多个呢 这是KMP不再适用 我们引入一个新的数据结构--字典树 (当然又有像AC自动机这样更 ...

  10. 一篇自己都看不懂的点分治&点分树学习笔记

    淀粉质点分治可真是个好东西 Part A.点分治 众所周知,树上分治算法有$3$种:点分治.边分治.链分治(最后一个似乎就是树链剖分),它们名字的不同是由于分治方式的不同的.点分治,顾名思义,每一次选 ...

随机推荐

  1. 函数计算 HTTP 触发器支持异步,解放双手搭建 Web 服务

    作者| 阿里云Serverless技术专家 澈尔 当前阿里云函数计算支持两种类型的函数:事件函数和 HTTP 函数.其中 HTTP 函数结合 HTTP 触发器,能够支持用户直接通过 HTTP 请求利用 ...

  2. 深度学习基础课: “判断性别”Demo需求分析和初步设计(下2)

    大家好~我开设了"深度学习基础班"的线上课程,带领同学从0开始学习全连接和卷积神经网络,进行数学推导,并且实现可以运行的Demo程序 线上课程资料: 本节课录像回放1 本节课录像回 ...

  3. 新手学习VUE——环境搭建及创建项目

    第一种方式: 1.     下载安装node.js 检查是否成功:node-v或npm-v 2..搭建项目: 第一种方法:用iview脚手架建项目 打开iview官网==>生态 ===>i ...

  4. Linux vim-go 开发环境搭建

    本文介绍 Linux 下 vim-go 的开发环境搭建.主要参考这篇博客进行的配置,其中记录了几个搭建环境时遇到的问题. 1. vim-go 开发环境搭建 1.1 用户隔离 由于使用的是共享宿主机,为 ...

  5. SV task and function

    内容 system verilog过程语句:自增和自减操作符 逻辑比较操作符 逻辑值为1bit inside语句 变量类型转换 强制类型转换:$cast() 变量位宽转换 变量符号位转换 for循环语 ...

  6. Go-连接redis

  7. linux环境C语言实现:h264与pcm封装成mp4视频格式

    前言 H.264是压缩过的数据,PCM是原始数据,MP4是一种视频封装格式.实际H.264与PCM不能直接合成MP4格式,因为音频格式不对.这里需要中间对音频做一次压缩处理.基本流程为:将PCM音频数 ...

  8. [转帖]JVM性能调优工具2之jcmd详解(覆盖全网最全的jcmd命令与说明文档)

    上篇文章里<JVM常用性能调优工具详解1>我们已经探究了jps.jstat等监控工具,以及jinfo.jmap.jstack.jhat等故障排查工具,这里我单独拿出一篇文章,特别介绍jcm ...

  9. 基于OpenJDK部署clickhouse-local镜像的快捷方法

    基于OpenJDK部署clickhouse-local镜像的快捷方法 摘要 前期搭建了一套基于OpenJDK的Clickhouse的服务端的镜像 可以简单使用dbeaver进行连接与使用. 后来发现需 ...

  10. [转帖]Linux fuse用户态文件系统及其libfuse

    https://www.jianshu.com/p/abc5524ac18c 为什么要有用户态文件系统 VFS文件系统可知文件系统在内核态的,应用程序操作文件,统一调用内核态的VFS层抽象接口. 突然 ...