TRIE:

在计算机科学中,Trie,又称前缀树或字典树,是一种有序树状的数据结构,用于保存关联数组,其中的键通常是字符串。——百度百科

自我理解:

trie树,是一种处理字符串前缀的数据结构,通常会有N*Len个节点,每个节点又引申出|S|个子节点指针,相当于一个很多叉的树,(甚至往往每个点叉的个数比高度还多)我们可以O(n)把待处理的字符串“挂到”trie上,最后统一查询,或者边挂边查。

可以发现,每个节点到根节点的路径就是一个前缀。

为什么要用字典树?

我们处理前缀问题的时候,往往需要求前缀的最值问题,公共前缀等等。朴素的做法都是要一个一个枚举,而我们把这些字符串集中到一个树上,通过公共前缀共用节点的特点,可以巧妙地不经过一一比较,就可以判断。

例如:

1.所有字符串LCP问题,朴素做法要处理hash,再在每个字符串上二分。logL* N,并且不能保证完全的正确性。毕竟有误差可能性。

通过trie树,相同的前缀已经被我们集中到了一起,我们只需要从根节点开始,一直找t[t[u].son].v==n的son节点,直到找不到为止,避免了对每个字符串进行操作的O(n)。

虽然预处理复杂度NlogL,但是查找的复杂度只有|S|*L,很少了。对于后续处理来说,预处理复杂度算不了什么。

2.两两字符串LCP问题:见例题:JZOJ 3126【GDKOI2013选拔】大LCP

但是,缺点很明显,trie的空间要更大,N太长就不行了。

需要的东西:

1.struct:son[28](如果是不分大小写的字典树),vis(该节点被访问过几次,或者:已经有多少个串拥有该节点代表的前缀,用于求LCP),num(编号为num的串结尾在这里,用于dfs确定字符串的字典序)2.insert:(洛谷2412)

void insert(char b[],int id)
{
int len=strlen(b);
int u=;
for(int i=;i<=len-;i++){
if(!t[u].son[tol(b[i])]) {
t[u].son[tol(b[i])]=++tot;
u=tot;
}
else u=t[u].son[tol(b[i])];
}
t[u].num=id;
}

id:字符串编号,注意每次从根节点0开始插入。第一个有实际意义的点必须从1开始,根什么都不代表,只有指针。但是有编号0。

3.dfs

void dfs(int x){
if(t[x].num) ran[t[x].num]=++cnt;
for(int i=;i<=;i++){
if(t[x].son[i]) dfs(t[x].son[i]);
}
}

确定所有插到trie上的字符串的字典序。

4.查找字符串,就直接找。

如果这个节点没有son[x[i]]这个出边,则返回没有;否则继续找,直到x[]找到底,判断这个点num是否为0,0返回没有,非0返回有。

5.查询任意两个字符串的LCP:两字符串对应的末尾节点,求LCA的深度,就是LCP

应用例题:(也有不是处理字符串的)

T1:JZOJ 3126【GDKOI2013选拔】大LCP

Description

LCP就是传说中的最长公共前缀,至于为什么要加上一个大字,那是因为…你会知道的。

首先,求LCP就要有字符串。既然那么需要它们,那就给出n个字符串好了。

于是你需要回答询问大LCP,询问给出一个k,你需要求出前k个字符串中两两的LCP最大值是多少,这就是传说中的大LCP。

Input

第一行一个整数N,Q,分别表示字符串个数和询问次数。

接下来N行,每行一个字符串。

再Q行,每行一个正整数k。

对于100%的数据,字符串总长度不超过10^6,1<=N,Q<=10^5.

分析:

这个题可以显著地体现trie求LCP的霸气所在。狂虐hash。

我们要是先都插入trie再处理询问,我怎么知道哪些是前k个产生的贡献?

询问是不强制在线的,所以把k从小到大排个序。按顺序插入,到了一个询问就输出正在更新的mx即可。

每次插入,直到到了一个要建新节点之前的所有经过的点,就是这个字符串与之前所有插入过的字符串的LCP长度。

例如叫做i字符串,都相当于是一个与1~i-1字符串进行LCP,直接O(1)带走啊。

相比较于hash,就可怜多了,必须n^2枚举字符串对,再二分LCP,n^2logL,哭死。

trie直接O(n),边插边查,复杂度大大下降。

前缀集合,trie确实优秀。

T2:poj2001

给定若干字符串,对于每个字符串求出一个最短前缀,使得这个前缀不是任何其他字符串的前缀。

分析:直接都挂上去,记录vis标记(见开头)。每个字符串按图索骥,直到某个点vis为1

T3:bzoj2251

给定一个长度为N的01串,要求按照字典序输出所有出现次数大于一次的子串的出现次数。
N<=3000。

分析:这个题有点想法。

我们知道,所有前缀的所有后缀就是所有子串,但是我们不能挂前缀啊,后缀怎么处理??

trie是处理前缀的。

不过还有一句话:所有后缀的所有前缀就是所有子串!!所以我们挂所有后缀。

节点标记vis,最后dfs按先0后1的顺序找到所有vis大于1的就行了。

为什么呢?因为vis=1,说明挂上的字符串中有一个从该点到根的前缀,而vis>1就说明有多个。

而我们挂上去的是后缀,有一个前缀,就有一个子串,有多个相同的前缀,就有多个相同的子串。等价转化。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
const int N=+;
struct trie{
int son[];
int v;
}t[N*N/];
int tot;
int n,len;
char a[N];
char b[N];
void add(char x[],int l){
//cout<<x<<endl;
int u=;
for(int i=;i<l;i++){
int b=x[i]-'';
if(t[u].son[b]){
u=t[u].son[b];
}
else{
t[u].son[b]=++tot;u=tot;
}
t[u].v++;
}
}
void dfs(int x){
if(t[x].v>) printf("%d\n",t[x].v);
if(t[x].son[]) dfs(t[x].son[]);
if(t[x].son[]) dfs(t[x].son[]);
}
int main()
{
scanf("%d",&n);
scanf("%s",a+);
for(int i=n;i>=;i--)
{
for(int j=i;j<=n;j++){
b[j-i]=a[j];
}
add(b,strlen(b));
//if(i!=1) memset(b,0,sizeof b);
}
dfs();
return ;
}

bzoj2251

T4:洛谷P4551

题目描述

给定一棵 n 个点的带权树,结点下标从 1 开始到 N 。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

数据范围

1≤n≤100000; 0<u,v≤n; 0≤w<2^31

分析:

这个题就比较考验洞察能力了。

因为异或运算满足交换律,结合律。所以我们可以求出每个点到根节点路径上的异或和dis[i]。

这样,我们要把所有的dis(二进制位)从高位到低位,挂到trie上。

对于节点i,先插入,再查询,将dis[i]高位补0对齐31位,从trie上往下找,每次先找有没有相反的,dis[i]这一位是0,找有没有1,反之找0

如果没有相反的,只能进入相同的了,然后指针后移,继续进行这个操作。

因为是从高位开始匹配,所以肯定尝试找在高位能异或出来1的可能性。

仍然利用前缀集合起来的性质,避免了枚举点对,O(n)扫一遍就好了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ui;
const int N=+;
const int M=;
int ch[*N][];
int tot=;
ui dis[N];
struct node{
int nxt,to;
ui val;
}bian[*N];
int hd[N];
int cnt;
int n;
ui ans,sum;
void add(int x,int y,ui z)
{
bian[++cnt].nxt=hd[x];
bian[cnt].to=y;
bian[cnt].val=z;
hd[x]=cnt;
}
void dfs(int x,ui dist,int fa)
{
dis[x]=dist;
for(int i=hd[x];i;i=bian[i].nxt)
{
int y=bian[i].to;
if(y==fa) continue;
if(x!=) dfs(y,dist^bian[i].val,x);
else dfs(y,bian[i].val,x);
}
}
ui work(ui x)
{
ui st=;
ui sum=;
int now=;
while(st)
{
int kk=((unsigned int)x&((unsigned int)<<st-))>>(st-);
if(ch[now][!kk]) {
now=ch[now][!kk],sum=sum+((unsigned int)<<st-);
}
else {
now=ch[now][kk];
}
st--;
}
return sum;
}
void puts(ui x)
{
ui st=;
int now=;
while(st)
{
int kk=((unsigned int)x&((unsigned int)<<st-))>>(st-);
if(ch[now][kk]) now=ch[now][kk];
else ch[now][kk]=++tot,now=tot;
st--;
}
}
signed main()
{
scanf("%lld",&n);
int x,y,z;
for(int i=;i<=n-;i++)
{
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(,,-);
for(int i=;i<=n;i++)
ans=max(ans,dis[i]);//warning!!
puts(dis[]);
for(int i=;i<=n;i++)
{
ans=max(ans,work(dis[i]));
puts(dis[i]);
}
printf("%lld",ans);
return ;
}

洛谷4551

TRIE 字典树 前缀紧急集合!的更多相关文章

  1. 算法导论:Trie字典树

    1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...

  2. 9-11-Trie树/字典树/前缀树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版

    课本源码部分 第9章  查找 - Trie树/字典树/前缀树(键树) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接☛☛☛ <数据结构-C语言版>(严蔚 ...

  3. 数据结构 -- Trie字典树

    简介 字典树:又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高. 性质:   1.  根节 ...

  4. 萌新笔记——C++里创建 Trie字典树(中文词典)(一)(插入、遍历)

    萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...

  5. Trie字典树 动态内存

    Trie字典树 #include "stdio.h" #include "iostream" #include "malloc.h" #in ...

  6. 标准Trie字典树学习二:Java实现方式之一

    特别声明: 博文主要是学习过程中的知识整理,以便之后的查阅回顾.部分内容来源于网络(如有摘录未标注请指出).内容如有差错,也欢迎指正! 系列文章: 1. 标准Trie字典树学习一:原理解析 2.标准T ...

  7. 817E. Choosing The Commander trie字典树

    LINK 题意:现有3种操作 加入一个值,删除一个值,询问pi^x<k的个数 思路:很像以前lightoj上写过的01异或的字典树,用字典树维护数求异或值即可 /** @Date : 2017- ...

  8. C++里创建 Trie字典树(中文词典)(一)(插入、遍历)

    萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...

  9. 踹树(Trie 字典树)

    Trie 字典树 ~~ 比 KMP 简单多了,无脑子选手学不会KMP,不会结论题~~ 自己懒得造图了OI WIKI 真棒 字典树大概长这么个亚子 呕吼真棒 就是将读进去的字符串根据当前的字符是什么和所 ...

随机推荐

  1. 将 C# 枚举序列化为 JSON 字符串 基础理论

    该转换过程需要引用 Newtonsoft.JSON,这其中的转换过程还是蛮有意思的. 一.定义枚举 /// <summary> /// 托寄物品枚举 /// </summary> ...

  2. vs感受,由于我的电脑装了俩年了!我直接写感受吧

    个人感受:最初的感觉,最开始装vs是因为我的电脑8.1不兼容vc6.0(一个挺坑的编程软件),最开始用vs的时候我还是一个小白什么都不懂,vs创建项目实在是太复杂,不看教程根本看不懂,也许是它能包容的 ...

  3. js实现树形内容展示

    1.首先这里有一个demo,里边有封装好的js文件.地址:http://files.cnblogs.com/files/feifeishi/dtree.zip 2.直接上代码 <div styl ...

  4. Ajax写成绩批量录入

    1.jsp,ajax的循环调用,必须要递归,否则会出错. <%@ page language="java" import="java.util.*" pa ...

  5. MySQL主从复制配置遇到的部分问题

    网上配置教程很多,我也是参考其他人的教程完成的,主要遇到了以下几个问题,如果以后有人遇到相同的希望能够给大家写提示吧. 1.my.cnf文件配置 Master上的my.cnf中配置的server_id ...

  6. Comparison of Static Code Analysis Tools for Java

    http://www.sw-engineering-candies.com/blog-1/comparison-of-findbugs-pmd-and-checkstyle https://stack ...

  7. Hadoop and net core a match made in docker

    https://blog.sixeyed.com/hadoop-and-net-core-a-match-made-in-docker/

  8. 安装wamp提示You dont't have permission to accesson on this server的解决方案

    展示一下安装好的效果图 首先找到安装目录下的路径[wamp\bin\apache\Apache2.2.21\conf\] § 找到httpd.conf,用记事本打开httpd.conf,然后将 1. ...

  9. Xshell 使用数字小键盘进行vim 写入操作.

    Copy From http://blog.csdn.net/shenzhen206/article/details/51200869 感谢原作者 在putty或xshell上用vi/vim的时候,开 ...

  10. 转载: 一、linux cpu、内存、IO、网络的测试工具

    来源地址: http://blog.csdn.net/wenwenxiong/article/details/77197997 记录一下 以后好找.. 一.linux cpu.内存.IO.网络的测试工 ...