据说这些并不对劲的内容是《信息学奥赛一本通提高篇》的配套练习。

并不会讲Trie树。

1.poj1056->

模板题。

2.bzoj1212->

设dp[i]表示T长度为i的前缀能否被理解。这样,对于所有满足T[(x+1)...i]是一个字典中的单词的x,dp[i]|=dp[x]。

所以,就可以将所有字典中的单词倒着建一棵Trie树,计算i时将T长度为i的前缀倒着在Trie树中匹配。

最后从后往前枚举i,第一个dp[i]=1的i就是答案。

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 2000010
#define maxnd 3010
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
int f=0;char ch[20];
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int ch[maxnd][26],cnt,go[maxnd],dp[maxn],n,m,ns,nt;
char t[maxnd],s[maxn];
int gx(char c){return c-'a';}
void ext()
{
int u=0;
for(int i=nt;i>=1;i--)
{
if(!ch[u][gx(t[i])])ch[u][gx(t[i])]=++cnt;
u=ch[u][gx(t[i])];
}
go[u]=1;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
{
scanf("%s",t+1);
nt=strlen(t+1);
ext();
}
//for(int i=0;i<=cnt;i++){for(int j=0;j<26;j++)if(ch[i][j])cout<<j<<":"<<ch[i][j]<<" ";cout<<endl;}
for(int i=1;i<=m;i++)
{
scanf("%s",s+1);
ns=strlen(s+1);dp[0]=1;
for(int j=1;j<=ns;j++)
{
int u=0;dp[j]=0;//cout<<gx(s[j])<<"*"<<endl;
for(int k=j;!dp[j]&&k>0&&ch[u][gx(s[k])];k--)
{
u=ch[u][gx(s[k])];
// cout<<go[u]<<endl;
if(go[u])dp[j]|=dp[k-1];
}
}
for(int j=ns;j>=0;j--)if(dp[j]){write(j);break;}
}
return 0;
}

3.bzoj1590->

听上去像裸题,但是很容易算晕。

先把所有密码前缀建成Trie树。

可以把FJ手里的每个串在所有密码前缀匹配的串分为两类:是它的前缀的串,它是这个串的前缀的串。

对于第一种,可以直接在Trie树中匹配,累加经过的点是几个串的结尾。

对于第二种,首先,如果在匹配过程中发现该走的边在Trie树中不存在,那么这一部分没有;如果匹配一直很顺利,最后走到了Trie树的一个节点x,那么答案就是子树x中一共有几个串的结尾。

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 500010
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
int f=0;char ch[20];
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int n,m,s[maxn],t[maxn],ch[maxn][2],val[maxn],w[maxn],ns,nt,cnt;
void ext()
{
int u=0;
for(int i=1;i<=nt;i++)
{
if(!ch[u][t[i]])ch[u][t[i]]=++cnt;
u=ch[u][t[i]];
}
val[u]++,w[u]++;
}
void dfs(int u)
{
for(int i=0;i<=1;i++)
if(ch[u][i])dfs(ch[u][i]),val[u]+=val[ch[u][i]];
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
{
nt=read();
for(int j=1;j<=nt;j++)t[j]=read();
ext();
}
dfs(0);
for(int i=1;i<=m;i++)
{
ns=read();
int u=0,f=0,cnt=0;
for(int j=1;j<=ns;j++)
{
s[j]=read();if(f)continue;
cnt+=w[u];
if(!ch[u][s[j]]){f=1;}
else u=ch[u][s[j]];
}
if(f)write(cnt);
else write(val[u]+cnt);
}
return 0;
}

4.bzoj4567->

这题听上去很复杂,但是会发现第一种完成方式肯定花费最高,而且存在一种方案能使得不会有第一种方式,那么肯定是不会有第一种方式的了。

根据第二种完成方式会发现对于某个单词,它对答案的贡献是$(它的序号)\times(1-以它为序号最大的后缀的单词数)$,所以,以它为序号最大的后缀的单词越多,这个单词就越该往后放,而且要放在以它为的后缀的单词的前面。

那么就可以将所有单词倒着建Trie树,并且进行路径压缩,使只剩下结束节点。最后的序列就是先走儿子数更少的儿子的dfs序。

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 510010
#define LL long long
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
void write(LL x)
{
int f=0;char ch[20];
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10ll+'0',x/=10ll;
while(f)putchar(ch[f--]);
putchar('\n');
}
int siz[maxn],ch[maxn][26],fa[maxn],cnt,tim,dfn[maxn],ns,n;
LL ans;
char s[maxn];
vector<pair<int ,int> >son[maxn];
int gx(char c){return c-'a';}
void ext()
{
int u=0;
for(int i=ns;i>=1;i--)
{
if(!ch[u][gx(s[i])])ch[u][gx(s[i])]=++cnt;
u=ch[u][gx(s[i])];
}
siz[u]++;
}
void getf(int u,int f)
{
if(siz[u])fa[u]=f,f=u;
for(int i=0;i<26;i++)if(ch[u][i])getf(ch[u][i],f);
}
void dfs(int u,int f)
{
if(u)ans+=(++tim)-f,f=tim;
sort(son[u].begin(),son[u].end());
int lim=son[u].size();
for(int i=0;i<lim;i++)dfs(son[u][i].second,f);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)scanf("%s",s+1),ns=strlen(s+1),ext();
getf(0,0);
for(int i=cnt;i>=1;i--)if(siz[i])siz[fa[i]]+=siz[i],son[fa[i]].push_back(make_pair(siz[i],i));
dfs(0,0);
write(ans);
return 0;
}

  

5.bzoj1954->

这道题实际上非常简单。因为给出的是一个树且不带修改,很容易可以想到树上的一条路径异或和可以由根两点的路径的异或和相互异或得到(根到lca的异或和会被抵消)。

那么将每个点的值转化为根到这个点路径的异或和,这道题便转化为给出n个数,找出两个数使其异或和最大。(很对劲的太刀流写的)

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<set>
#include<queue>
#define maxn 100010
#define maxm (maxn<<1)
#define maxnd 3000010
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
int f=0;char ch[20];
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int cnt,fir[maxn],nxt[maxm],v[maxm],w[maxm],ans;
int num[maxn],ch[maxnd][2],n,cntnd;
void ade(int u1,int v1,int w1){v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++;}
void getn(int u,int fa)
{
for(int k=fir[u];k!=-1;k=nxt[k])
if(v[k]!=fa)num[v[k]]=num[u]^w[k],getn(v[k],u);
}
void ext(int x)
{
int u=0;
for(int i=30;i>=0;i--)
{
int tmp=x&(1<<i)?1:0;
if(!ch[u][tmp])ch[u][tmp]=++cntnd;
u=ch[u][tmp];
}
}
int getans(int x)
{
int u=0,ans=0;
for(int i=30;i>=0;i--)
{
int tmp=x&(1<<i)?1:0;
if(ch[u][tmp^1])u=ch[u][tmp^1],ans+=(tmp^1)*(1<<i);
else u=ch[u][tmp],ans+=(tmp)*(1<<i);
}
return ans;
}
int main()
{
memset(fir,-1,sizeof(fir));
n=read();
for(int i=1;i<n;i++){int x=read(),y=read(),z=read();ade(x,y,z),ade(y,x,z);}
getn(1,0);
for(int i=1;i<=n;i++)ext(num[i]);
for(int i=1;i<=n;i++)
{
int tmp=getans(num[i]);
ans=max(ans,num[i]^tmp);
}
write(ans);
return 0;
}

并不对劲的字符串专题(三):Trie树的更多相关文章

  1. 大规模字符串检索-压缩trie树

    本文使用压缩trie树实现字符串检索的功能.首先将字符串通过编码转化为二进制串,随后将二进制串插入到trie树中,在插入过程中同时实现压缩的功能. 字符编码采用Huffman,但最终测试发现不采用Hu ...

  2. 并不对劲的字符串专题(二):kmp

    据说这些并不对劲的内容是<信息学奥赛一本通提高篇>的配套练习. 先感叹一句<信息学奥赛一本通提高篇>上对kmp的解释和matrix67的博客相似度99%(还抄错了),莫非mat ...

  3. 835. 字符串统计(Trie树模板题)

    维护一个字符串集合,支持两种操作: “I x”向集合中插入一个字符串x: “Q x”询问一个字符串在集合中出现了多少次. 共有N个操作,输入的字符串总长度不超过 105105,字符串仅包含小写英文字母 ...

  4. Trie树|字典树(字符串排序)

    有时,我们会碰到对字符串的排序,若采用一些经典的排序算法,则时间复杂度一般为O(n*lgn),但若采用Trie树,则时间复杂度仅为O(n). Trie树又名字典树,从字面意思即可理解,这种树的结构像英 ...

  5. [转] Trie树详解及其应用

    一.知识简介         最近在看字符串算法了,其中字典树.AC自动机和后缀树的应用是最广泛的了,下面将会重点介绍下这几个算法的应用.       字典树(Trie)可以保存一些字符串->值 ...

  6. Trie树详解及其应用

    一.知识简介        最近在看字符串算法了,其中字典树.AC自动机和后缀树的应用是最广泛的了,下面将会重点介绍下这几个算法的应用.      字典树(Trie)可以保存一些字符串->值的对 ...

  7. 【动画】看动画轻松理解「Trie树」

    Trie树 Trie这个名字取自“retrieval”,检索,因为Trie可以只用一个前缀便可以在一部字典中找到想要的单词. 虽然发音与「Tree」一致,但为了将这种 字典树 与 普通二叉树 以示区别 ...

  8. Trie树(Prefix Tree)介绍

    本文用尽量简洁的语言介绍一种树形数据结构 -- Trie树. 一.什么是Trie树 Trie树,又叫字典树.前缀树(Prefix Tree).单词查找树 或 键树,是一种多叉树结构.如下图: 上图是一 ...

  9. 数据结构与算法—Trie树

    Trie,又经常叫前缀树,字典树等等.它有很多变种,如后缀树,Radix Tree/Trie,PATRICIA tree,以及bitwise版本的crit-bit tree.当然很多名字的意义其实有交 ...

随机推荐

  1. php引入PHPMailer发送邮件

    昨天做了一个发送邮件的功能,如果直接用mail()函数,需要拥有自己的邮件服务器,所有引入PHPMailer类方便快捷,简单写一下开发步骤: 一.拥有自己的邮箱账号(作为发件人邮箱) 分两种情况: 1 ...

  2. POJ1703-Find them, Catch them 并查集构造

                                             Find them, Catch them 好久没有做并查集的题,竟然快把并查集忘完了. 题意:大致是有两个监狱,n个 ...

  3. UML的关联(Association), 聚合(Aggregation), 组合(Composition)区别

    转载:http://blog.csdn.net/ocean181/article/details/6117369 UML的关联(Association), 聚合(Aggregation), 组合(Co ...

  4. 最大和(codevs 1648)

    题目描述 Description N个数围成一圈,要求从中选择若干个连续的数(注意每个数最多只能选一次)加起来,问能形成的最大的和. 输入描述 Input Description 第一行输入N,表示数 ...

  5. 【POJ3264】Balanced Lineup(RMQ)

    题意:每天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. 有一天, John 决定让一些牛们玩一场飞盘比赛. 他准备找一群在对列中为置连续的牛来进行比赛 ...

  6. Delphi接口使用实例介绍

    对于Object Pascal语言来说,最近一段时间最有意义的改进就是从Delphi3开始支持接口(interface),接口定义了能够与一个对象进行交互操作的一组过程和函数.对一个接口进行定义包含两 ...

  7. Codeforces 938G(cdq分治+可撤销并查集+线性基)

    题意: 有一个无向连通图,支持三个操作: 1 x y d : 新建一条x和y的无向边,长度为d 2 x y    :删除x和y之间的无向边 3 x y    :询问x到y的所有路径中(可以绕环)最短的 ...

  8. MySQL入门笔记 - 数据类型

    参考书籍<MySQL入门很简单> 数据类型是数据的一种属性,可以决定数据的存储方式.有效范围和相应的限制. 1.整数类型   1.1 MySQL的整数类型 MySQL中int类型和inte ...

  9. hdu1181 dfs搜索之变形课

    原题地址 这道题数据据说比較水,除了第一组数据是Yes以外.其余都是No.非常多人抓住这点就水过了.当然了,我认为那样过了也没什么意思.刷oj刷的是质量不是数量. 这道题从题目上来看是个不错的 搜索题 ...

  10. ICONFONT在APP中的使用

    阿里IconFont平台 http://www.iconfont.cn/ 这里是阿里巴巴UED部门开发的IconFont平台,眼下阿里系的重量级产品都在使用,里面有非常多资源可供使用. 这里说说怎样在 ...