关于\(Trie\)树的详细介绍,还请移步这篇深度好文

基本操作

插入

void insert()
{
int p=0;
int len=strlen(tmp+1);
for(int i=1;i<=len;i++)
{
int qwq=tmp[i]-'0';
if(!trie[p][qwq]) trie[p][qwq]=++tot;
p=trie[p][qwq];
}
}

注意,其中\(tot=0\),我习惯\(p\)初始值也为\(0\)。\(tot\)与\(p\)初值应保持一致。

检索操作非常灵活,各题不同,但是都很简单,那么就自己想吧=w=(不负责地逃离)。

几道例题

简单题1:Luogu P3879 [TJOI2010]阅读理解

在每个文章记录一下出现次数即可。

#include<cstdio>
#include<algorithm>
#include<cstring> using namespace std; int n,m,l,p,tot=1;
int trie[500090][26];
bool b[500000][1100];
char word[25]; void insert(char *str,int opt)
{
int len=strlen(str);p=1;
for(int k=0;k<len;k++)
{
int ch=str[k]-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;
p=trie[p][ch];
}
b[p][opt]=1;
} void ask(char *str)
{
int len=strlen(word);p=1;
bool flag=1;
for(int k=0;k<len;k++)
{
if(!trie[p][str[k]-'a'])
{
flag=0;break;
}
p=trie[p][str[k]-'a'];
}
if(flag)
{
for(int i=1;i<=n;i++)
if(b[p][i])
printf("%d ",i);
}
} int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&l);
for(int j=1;j<=l;j++)
scanf("%s",word),insert(word,i);
}
scanf("%d",&m);
while(m--)
{
scanf("%s",word);
ask(word);
printf("\n");
}
return 0;
}

简单题2:UVA11362 Phone List

给定\(n\)个长度不超过\(10\)的数字串,判断是否有两个字符串\(A\)和\(B\),满足\(A\)是\(B\)的前缀。

满足题目中的条件对于每个串有两种情况:当这个串没读完的时候,有字符串以当前指针为结尾,说明之前串是当前串的前缀;这个串读完后,在计数数组中已经有记录,说明当前串是之前串的前缀。第一种情况开始被我忽略了。

分析完这点后,注意多组数据清空,空间开合适就好了。但是这题特别坑,存在的情况输出\(NO\),不存在输出\(YES\)。被坑了233.

#include<cstdio>
#include<algorithm>
#include<cstring> using namespace std; int T,n,tot,lim;
int trie[200900][20],endd[200090],cnt[200090];
bool flag;
char tmp[20]; void in_trie()
{
int p=0,len=strlen(tmp+1);
for(int i=1;i<=len;i++)
{
int qwq=tmp[i]-'0';
if(!trie[p][qwq]) trie[p][qwq]=++tot;
p=trie[p][qwq];
cnt[p]++;
if(endd[p]) flag=1;
}
endd[p]=1;
if(cnt[p]>1) flag=1;
} int main()
{
scanf("%d",&T);
while(T--)
{
flag=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",tmp+1);
in_trie();
}
if(flag) printf("NO\n");
else printf("YES\n");
memset(endd,0,sizeof(endd));
memset(trie,0,sizeof(trie));
memset(cnt,0,sizeof(cnt));
tot=0;
}
return 0;
}

话说做这道题的时候开始用的数组叫\(end\)然后\(Compile\) \(error\)了...虚的一批。

简单题3:Luogu P2580 于是他错误的点名开始了

非常符合字典树的性质,直接上就行了。

#include<bits/stdc++.h>
using namespace std;
int n,m,tot,p;
int flag,end[1000090];
int trie[1000090][27];
char a[100];
void insert(char *str)
{
int len=strlen(str);
p=1;
for(int k=0;k<len;k++)
{
int ch=str[k]-'a';
if(trie[p][ch]==0) trie[p][ch]=++tot;
p=trie[p][ch];
}
end[p]=1;
}
bool search(char *str)
{
int len=strlen(str),p=1;
for(int k=0;k<len;k++)
{
p=trie[p][str[k]-'a'];
if(p==0) return false;
}
flag=p;
return true;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",a);
insert(a);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",a);
if(!search(a))
{
printf("WRONG\n");
continue;
}
else
{
if(end[flag]==1)
{
printf("OK\n");
end[flag]++;
continue;
}
printf("REPEAT\n");
}
}
return 0;
}

巧妙运用:\(01trie\)树

Luogu P4551 最长异或路径

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

先考虑一个简化问题:给你一个数列,在其中任选两个数异或,求能获得的异或最大值。

考虑把\(32\)位的二进制\(01\)串强行塞到\(trie\)树上,这个树上的所有元素只可能为\(0\)或\(1\)。那么我们可以对于每个元素,在字典树上进行检索,每次尝试访问与这个元素的这个位的相反的指针,若指针指向空,那么只能访问和这位相同的指针。因为异或运算时“相同得1不同得0”的,所以这样能保证最优。

回到我们原来的问题之前,我们看另一道题:Luogu P2420 让我们异或吧。求两点间路径的所有边权的异或值。设\(d[i]\)为根节点到当前节点路径上的异或值。那么对于\((x,y)\)答案就是\(d[x]\) \(xor\) \(d[y]\)。为什么不用考虑他们的\(lca\)以上的部分呢,以为他们相同,一异或就会变成0了==。

代码还是稍微给一下?(虽然和今天的内容关系不大)

#include<bits/stdc++.h>
using namespace std;
int n,m,tot;
int head[200009];
int xxor[200009],visit[200009];
struct node{
int to,val,next;
}edge[200009];
queue<int>q;
void add(int x,int y,int z)
{
edge[++tot].to=y;
edge[tot].val=z;
edge[tot].next=head[x];
head[x]=tot;
}
void bfs()
{
//queue<int>q;
q.push(1);
visit[1]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
if(visit[y]) continue;
visit[y]=1;
xxor[y]=xxor[x]^edge[i].val;
q.push(y);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int u=0,v=0,w=0;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
bfs();
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int u=0,v=0,ans=0;
scanf("%d%d",&u,&v);
printf("%d\n",xxor[u]^xxor[v]);
}
return 0;
}

回到我们最开始的问题==!有了前面两题的基础,那么我们在这个问题中要求的答案就等效于简化问题,只是数列变成了\(d[]\)。

#include<cstdio>
#include<algorithm> using namespace std; int n,tot,ans;
int d[100090],head[100090],trie[100090*32][5];
struct node{
int to,next,val;
}edge[200090]; void add(int x,int y,int z)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
edge[tot].val=z;
} void dfs(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
d[v]=d[u]^edge[i].val;
dfs(v,u);
}
} void insert(int x)
{
int p=0;
for(int i=(1<<30);i;i>>=1)
{
bool qwq=x&i;//注意用bool
if(!trie[p][qwq]) trie[p][qwq]=++tot;
p=trie[p][qwq];
}
} int ask(int x)
{
int p=0,orz=0;
for(int i=(1<<30);i;i>>=1)
{
bool qwq=x&i;
if(trie[p][qwq^1]) orz+=i,p=trie[p][qwq^1];
else p=trie[p][qwq];
}
return orz;
} int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int x=0,y=0,z=0;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
dfs(1,0);tot=0;
for(int i=1;i<=n;i++) insert(d[i]);
for(int i=1;i<=n;i++) ans=max(ans,ask(d[i]));
printf("%d\n",ans);
return 0;
}

一定要注意\(qwq\)是\(bool\)类型的!这样保证只有0或1。如果是\(int\)的话,就不保证了!!(\(Warning\)

最后的最后

讨论下开空间的问题吧!

本蒟蒻几乎是做一道\(trie\)就\(RE\)一次...(逃)

比较玄学...尽量开大一点好(逃)

Trie树(小)总结 By cellur925的更多相关文章

  1. Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )

    #1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助, ...

  2. 基于trie树的具有联想功能的文本编辑器

    之前的软件设计与开发实践课程中,自己构思的大作业题目.做的具有核心功能,但是还欠缺边边角角的小功能和持久化数据结构,先放出来,有机会一点点改.github:https://github.com/chu ...

  3. Trie树-字典查找

    描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题: ...

  4. [转]双数组TRIE树原理

    原文名称: An Efficient Digital Search Algorithm by Using a Double-Array Structure 作者: JUN-ICHI AOE 译文: 使 ...

  5. 1014 : Trie树 hihocoder

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. ...

  6. Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结

    Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 ...

  7. Hihicoder 题目1 : Trie树(字典树,经典题)

    题目1 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编 ...

  8. 【BZOJ3439】Kpm的MC密码 trie树+主席树

    Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身 ...

  9. Trie树

    一.什么是trie树 1.Trie树 (特例结构树)   Trie树,又称单词查找树.字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构.典型应用是用于统计和排序大量的字符串( ...

随机推荐

  1. ABAP 将Range 条目数转化

    RANGES:r_vbeln FOR lips-vbeln. r_vbeln-sign = 'I'. r_vbeln-option = 'EQ'. LOOP AT gt_item INTO gw_it ...

  2. Safair 浏览器cllick事件不生效或者需要双击才生效

    针对Safair 浏览器cllick事件不生效或者需要双击才生效的解决方案. 方法一:给元素加上cursor: pointer样式.(不生效) 方法二:ios事件机制不一样,将click事件改为mou ...

  3. Redis C语言操作封装

    #ifndef BOYAA_FOURLANDLORD_REDISCLASS_H_20130217 #define BOYAA_FOURLANDLORD_REDISCLASS_H_20130217 #i ...

  4. POJ 2506 Tiling (递推 + 大数加法模拟 )

    Tiling Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7965   Accepted: 3866 Descriptio ...

  5. SCAU 1138 代码等式 并查集

    1138 代码等式[附加题] 该题有题解 时间限制:500MS  内存限制:65536K 提交次数:59 通过次数:21 题型: 编程题   语言: G++;GCC Description 一个代码等 ...

  6. Codeforces Round #398 (Div. 2) C. Garland —— DFS

    题目链接:http://codeforces.com/contest/767/problem/C 题解:类似于提着一串葡萄,用剪刀剪两条藤,葡萄分成了三串.问怎样剪才能使三串葡萄的质量相等. 首先要做 ...

  7. CISCO-更新路由器IOS

    1,查看flash,复制IOS文件名,再上传IOS 2,传送完毕查看下flash Router# show flash: 查看flash中的信息 Directory of flash: 1 -rw- ...

  8. C语言教学杂记——字母排序

    一个人在被告诉一个问题应该怎么被解决后,而且亲身试验效果OK后,一旦遇到类似的问题,就会条件反射般直接拿这个方法来用了.很少会去想为什么要用这个方法,会不会有什么隐患,还有没有别的方法呢,等等这些问题 ...

  9. 局域网 —— VLAN

    0. network 简单分类 LAN:Local Area Network,局域网: WLAN:Wireless LAN(Local Area Network), VLAN:Virtual LAN( ...

  10. 页面刷新 方法总结 JSP刷新[转]

    1) <meta http-equiv="refresh"content="10;url=跳转的页面"> 10表示间隔10秒刷新一次 2) < ...