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

并不会讲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. zoj 1251 Box of Bricks

    Box of Bricks Time Limit: 2 Seconds      Memory Limit: 65536 KB Little Bob likes playing with his bo ...

  2. UVA10200-Prime Time/HDU2161-Primes,例题讲解,牛逼的费马小定理和欧拉函数判素数。

                                                    10200 - Prime Time 此题极坑(本菜太弱),鉴定完毕,9遍过. 题意:很简单的求一个区间 ...

  3. [Vijos1617] 超级教主(DP + 单调队列)

    传送门 设 f[i] 表示吃完 f[i] 及其以下的能量球后所剩下的能量. 所以 f[i] = max(f[i], f[j] + (sum[i] - sum[j]) - i * 100) ( 0 &l ...

  4. 【贪心+博弈】C. Naming Company

    http://codeforces.com/contest/794/problem/C 题意:A,B两人各有长度为n的字符串,轮流向空字符串C中放字母,A尽可能让字符串字典序小,B尽可能让字符串字典序 ...

  5. 【BZOJ3939】Cow Hopscotch(动态开点线段树)

    题意: 就像人类喜欢跳格子游戏一样,FJ的奶牛们发明了一种新的跳格子游戏.虽然这种接近一吨的笨拙的动物玩跳格子游戏几乎总是不愉快地结束,但是这并没有阻止奶牛们在每天下午参加跳格子游戏  游戏在一个R* ...

  6. 获取鼠标位置的几个通用的JS函数

    原文:http://www.open-open.com/code/view/1421401009218 /*两个通用函数,用于获取鼠标相对于整个页面的当前位置*/ function getX(e) { ...

  7. 代理服务器squid简介

    Squid 是一个高性能.开源的代理缓存服务器和 Web 缓存进程,支持 FTP.Internet Gopher.HTTPS 和 SSL 等多种协议.它通过一个非阻塞的.I/O 事件驱动的单一进程处理 ...

  8. C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用

    序列性容器::(vector和list和deque)   erase迭代器不仅使所有指向被删元素的迭代器失效,而且使被   删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方 式, ...

  9. 淘宝API学习之道:淘宝TOP之API接口接入教程

    作为一个中小型站点开发人员,淘宝API的开放大大缩短了站点的开发周期和运作效率.面对海量的数据.开发人员仅仅要细致阅读开发文档,熟悉对应的接口,就能够把数据导入自己的站点,这样就不必望洋兴叹了. 眼下 ...

  10. 【剑指Offer】俯视50题之21 - 30题

    面试题21包括min函数的栈  面试题22栈的压入.弹出序列  面试题23从上往下打印二叉树  面试题24二叉搜索树的后序遍历序列  面试题25二叉树中和为某一值的路径  面试题26复杂链表的复制  ...