CODEVS 2542单词__fail树

2542 单词

2013年省队选拔赛天津市队选拔赛

时间限制: 2 s

空间限制: 256000 KB

题目等级 : 大师 Master

题目描述 Description

小张最近在忙毕业,所以一直在读论文。一篇论文是由许多单词组成的。

但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。

输入描述 Input Description

第一行一个整数N,表示有N个单词,接下来N行,每行一个单词,每个单词都由小写字母组成(N<=200)

输出描述 Output Description

输出N个整数,第i行的数表示第i个单词在文章中出现了多少次。

样例输入 Sample Input

3
a
aa
aaa

样例输出 Sample Output

6

3

1

数据范围及提示 Data Size & Hint

30%的数据 单词总长度不超过10^3
100%的数据 单词总长度不超过10^6

————————————————————————————————————————————————————————————————————————————————

一看是AC自动机是没有问题的,但是建完AC自动机后该如何做呢?

当然可以直接用AC自动机的fail指针向前跳,但是这样要枚举单词内的每个位置,严重超时。

这里要用到一个很有用的工具——fail树。

当AC自动机建完以后,我们发现每一个节点都只有一个失败指针(根节点的可以忽略),且沿着失败指针最终能走到根节点。

这样我们将失败指针反向就可以变成一棵树。

这棵树的有些重要的性质:

1、以x节点为根的子树中每个节点代表的字符串都以x点所代表的字符串为后缀。

2、节点x代表的字符串的最大后缀为父节点代表的字符串。

这样我们在插入字符串是把经过的每个节点的val值都加1,就相当于统计的对应前缀出现的次数。

然后通过dfs将val变为val加它所有子树的val和。也就是以(s)为前缀的字符串个数+以(*+s)为前缀的字符串的个数+以(**+s)为前缀的个数+。。。。。

这样s出现的次数就是s串尾对应的val值。

比较难想,但是fail树真的很有用。

————————————————————————————————————————————————————————————————————————————————

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e6+3;
 4 const int maxs=26;
 5 struct edge{
 6     int u,v,next;
 7 }e[maxn];
 8 int head[maxn],js=0;
 9 void addage(int u,int v)
10 {
11     e[++js].u=u;e[js].v=v;
12     e[js].next=head[u];head[u]=js;
13 }
14 struct AC{
15     int ch[maxn][maxs];
16     int val[maxn],f[maxn];
17     int sz;
18     AC(){
19         memset(ch[0],0,sizeof(ch[0]));
20         val[0]=0;
21         sz=1;
22     }
23     int idx(char c){
24         return c-‘a‘;
25     }
26     int insert(char *s){
27         int n=strlen(s),cur=0;
28         for(int i=0,c;i<n;i++){
29             c=idx(s[i]);
30             if(!ch[cur][c]){
31                 memset(ch[sz],0,sizeof(sz));
32                 val[sz]=0;
33                 ch[cur][c]=sz++;
34             }
35             cur=ch[cur][c];
36             val[cur]++;
37         }
38         return cur;
39     }
40     void getfail(){
41         queue<int>q;
42         f[0]=0;
43         for(int c=0;c<maxs;c++){
44             int v=ch[0][c];
45             if(v){
46                 f[v]=0;
47                 q.push(v);
48                 addage(0,v);
49             }
50         }
51         while(!q.empty()){
52             int u=q.front();q.pop();
53             for(int c=0;c<maxs;++c){
54                 int v=ch[u][c];
55                 if(v){
56                     q.push(v);
57                     int r=f[u];
58                     while(r && !ch[r][c])r=f[r];
59                     f[v]=ch[r][c];
60                     addage(f[v],v);
61                 }
62             }
63         }
64     }
65 }ac;
66 int n;
67 int wz[201];
68 char s[maxn];
69 void dfs(int u,int f)
70 {
71     for(int i=head[u];i;i=e[i].next){
72         int u=e[i].u,v=e[i].v;
73         if(v==f)continue;
74         dfs(v,u);
75         ac.val[u]+=ac.val[v];
76     }
77 }
78 int main()
79 {
80     scanf("%d",&n);
81     for(int i=0;i<n;++i){
82         scanf("%s",s);
83         wz[i]=ac.insert(s);
84     }
85     ac.getfail();
86     dfs(0,-1);
87     for(int i=0;i<n;i++)
88         printf("%d\n",ac.val[wz[i]]);
89     return 0;
90 }

时间: 2024-12-15 12:01:50

CODEVS 2542单词__fail树的相关文章

codevs 1729 单词查找树

时间限制: 2 s 空间限制: 128000 KB 题目等级 : 大师 Master 题解 题目描述 Description 在进行文法分析的时候,通常需要检测一个单词是否在我们的单词列表里.为了提高查找和定位的速度,通常都要画出与单词列表所对应的单词查找树,其特点如下: l  根节点不包含字母,除根节点外每一个节点都仅包含一个大写英文字母: l  从根节点到某一节点,路径上经过的字母依次连起来所构成的字母序列,称为该节点对应的单词.单词列表中的每个词,都是该单词查找树某个节点所对应的单词: l

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

hbase源码系列(五)单词查找树

在上一章中提到了编码压缩,讲了一个简单的DataBlockEncoding.PREFIX算法,它用的是前序编码压缩的算法,它搜索到时候,是全扫描的方式搜索的,如此一来,搜索效率实在是不敢恭维,所以在hbase当中单独拿了一个工程出来实现了Trie的数据结果,既达到了压缩编码的效果,亦达到了方便查询的效果,一举两得,设置的方法是在上一章的末尾提了. 下面讲一下这个Trie树的原理吧. hbase源码系列(五)单词查找树,布布扣,bubuko.com

单词查找树

单词查找树 分析: 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 using namespace std; 5 int i,j,n,t,k; 6 string a[8001];//数组可以达到32*1024=32768,一个单词一个字节 ,一个换行符2个字节 7 string s; 8 int main() 9 { 10 //freopen("word.txt","

[NOI2000] 单词查找树

★★   输入文件:trie.in   输出文件:trie.out   简单对比 时间限制:1 s   内存限制:128 MB 在进行文法分析的时候,通常需要检测一个单词是否在我们的单词列表里.为了提高查找和定位的速度,通常都要画出与单词列表所对应的单词查找树,其特点如下: 根节点不包含字母,除根节点外每一个节点都仅包含一个大写英文字母: 从根节点到某一节点,路径上经过的字母依次连起来所构成的字母序列,称为该节点对应的单词.单词列表中的每个词,都是该单词查找树某个节点所对应的单词: 在满足上述条

Trie树,又称单词查找树、字典

在百度或淘宝搜索时,每输入字符都会出现搜索建议,比如输入"北京",搜索框下面会以北京为前缀,展示"北京爱情故事"."北京公交"."北京医院"等等搜索词.实现这类技术后台所采用的数据结构是什么?[中国某著名搜索引擎B公司2012年6月笔试题] 答案:Trie树,又称单词查找树.字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系

Trie树&mdash;字典树(单词查找树)

Trie树,又称字典树,单词查找树.它来源于retrieval(检索)中取中间四个字符构成的.用于存储大量的字符串以便支持快速模式匹配.主要应用在信息检索领域. Trie有三种结构:标准Trie(standard trie),压缩Trie,后缀Trie(suffix trie). 1.标准Trie 标准Trie树的结构:所有含有公共前缀的字符串将挂在树中同一个结点下.实际上trie简明的存储于串集合汇总的所有公共前缀.加入有这样一个字符串集合X{bear,bell,bid,bull,buy,se

洛谷P2412 查单词 [trie树 RMQ]

题目背景 滚粗了的HansBug在收拾旧英语书,然而他发现了什么奇妙的东西. 题目描述 udp2.T3如果遇到相同的字符串,输出后面的 蒟蒻HansBug在一本英语书里面找到了一个单词表,包含N个单词(每个单词内包含大小写字母).现在他想要找出某一段连续的单词内字典序最大的单词. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示单词个数和询问个数. 接下来N行每行包含一个字符串,仅包含大小写字母,长度不超过15,表示一个单词. 再接下来M行每行包含两个整数x.y,表示求从第x到第y

BZOJ 3172 Tjoi2013 单词 fail树

题目大意及后缀数组做法见 http://blog.csdn.net/popoqqq/article/details/41042473 原来正解是fail树--难怪后缀数组被卡成这样 首先我们将给出的n个串构建AC自动机 朴素的做法是对于每个串将这个串每个节点沿着fail指针扫一遍,将路径上的所有点的cnt++ 但是这样做会TLE 我们不妨反向思考 fail指针反向后是一棵树 沿着fail指针扫一遍就是沿着树边向根扫一遍 只在插入时将每个串的每个节点cnt++ 那么每个串终点所在fail树的子树中