Trie 图

时间限制:20000ms

单点时限:1000ms

内存限制:512MB

描述

前情回顾

上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从互联网上收集来的文章,和一本厚厚的河蟹词典,而他们要做的是判断这篇文章中是否存在那些属于河蟹词典中的词语。

当时,小Hi和小Ho的水平还是十分有限,他们只能够想到:“枚举每一个单词,然后枚举文章中可能的起始位置,然后进行匹配,看能否成功。”这样非常朴素的想法,但是这样的算法时间复杂度是相当高的,如果说词典的词语数量为N,每个词语长度为L,文章的长度为M,那么需要进行的计算次数是在N*M*L这个级别的,而这个数据在河蟹先生看来是不能够接受的。

于是河蟹先生决定先给他们个机会学习一下,于是给出了一个条件N=1,也就是说词典里面事实上只有一个词语,但是希望他们能够统计这个词语在文章中出现的次数,这便是我们常说的模式匹配问题。而小Hi和小Ho呢,通过这一周的努力,学习钻研了KMP算法,并在互相帮助之下,已经成功的解决掉了这个问题!

这便是Hiho一下第三周发生的事情,而现在第四周到了,小Hi和小Ho也要踏上解决真正难题的旅程了呢!

任务回顾

小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。

这一天,他们……咳咳,说远了,且说小Ho好不容易写完了第三周程序,却发现自己错过了HihoCoder上的提交日期,于是找小Hi哭诉,小Hi虽然身为管理员,但是也不好破这个例,于是把小Ho赶去题库交了代码,总算是哄好了小Ho。

小Ho交完程序然后屁颠屁颠的跑回了小Hi这边,问道:“小Hi,你说我们是不是可以去完成河蟹大大的任务了呢?”

小Hi思索半天,道:“老夫夜观星象……啊不,我这两天查阅了很多资料,发现这个问题其实也是很经典的问题,早在06年就有信息学奥林匹克竞赛国家集训队的论文中详详细细的分析了这一问题,而他们使用的就是Trie图这样一种数据结构!”

“Trie图?是不是和我们在第二周遇到的那个Trie树有些相似呀?”小Ho问道。

“没错!Trie图就是在Trie树的基础上发展成的一种数据结构。如果要想用一本词典构成Trie图的话,那么就首先要用这本词典构成一棵Trie树,然后在Trie树的基础上添加一些边,就能够变成Trie图了!”小Hi又作老师状。

“哦!但是你说了这么多,我都不知道Trie图是什么样的呢!”小Ho无奈道。

“也是!那我们还是从头开始,先讲讲怎么用Trie树来解决这个问题,然后在Trie树的基础上,讨论下一步应该如何。”小Hi想了想说道。

提示一:如何用Trie树进行“河蟹”

“现在我们有了一个时间复杂度在O(ML)级别的方法,但是我们的征途在星辰大海,啊不,我们不能满足于这样一个60分的方法。所以呢,我们还是要贯彻我们一贯的做法,寻找在这个算法中那些冗余的计算!“小Hi道:”那么我们现在来看看Trie树进行计算的时候都发生了些什么。”

提示二:Trie树的优化思路——后缀结点

“那么现在……”小Hi刚要开口,就被小Ho无情打断。

“可是小Hi老师~你看在这种情况下,结点C找不到对应的后缀结点,它对应的路径是aaabc,而aabc在Trie里面是走不出来的!”小Ho手中挥舞着一张纸,问道。

“你个瓜娃子,老是拆老子台做啥子!……阿不,小Ho你别担心,我这就要讲解如何求后缀结点呢~”小Hi笑容满面的说道。

提示三:如何求解Trie树中每个结点的后缀结点

“原来如此!这样我就知道了每一个结点的后缀结点了,接下来我就可以很轻松的解决河蟹先生交给我的问题了呢!”小Ho高兴的说道:“但是,说好的Trie图在哪里呢?”

小Hi不由笑道:“你这叫买椟还珠你知道么?还记得我们再计算后缀结点的时候计算出的从每个点出发,经由每一个char(比如‘a‘..‘d‘)会走到的结点么?把这些边添加到Trie树上,就是Trie图了!”

“原来是这样,但是这些边感觉除了计算后缀结点之外,没有什么用处呀?”小Ho又开始问问题了。

“这就是Trie图的巧妙之处了,你想想你什么时候需要知道一个结点的后缀结点?”小Hi实在不忍看自己的兄弟这般呆萌,只能耐着性子解释。

小Ho顿时恍然大悟,“在这个结点不能够继续和文章str继续匹配了的时候,也就是这个结点没有“文章的下一个字符”对应的那条边,哦!我知道了,在Trie图中,每个结点都补全了所有的边,所以原来需要先找到后缀结点再根据“str的下一个字符”这样一条边找到下一个结点,现在可以直接通过当前结点的“str的下一个字符”这样一条边就可以接着往下匹配了,如果本来是有这条边的,那不用多说,而如果这条边是根据后缀结点补全的,那便是我们想要的结果!

“所以呢!完成这个任务的方法总的来说就是这样,先根据字典构建一棵Trie树,然后根据我们之前所说的构建出对应的Trie图,然后从Trie图的根节点开始,沿着文章str的每一个字符,走出对应的边,直到遇到一个标记结点或者整个str都已经匹配完成了~”小Hi适时的总结道。

“而这样的时间复杂度则在O(NL+M)级别的呢!想来是足以完成河蟹先生的要求了呢~”小Ho搬了搬手指,说道。

“是的!但是河蟹先生要求的可不是想法哦,他可是希望我们写出程序给它呢!”

输入

每个输入文件有且仅有一组测试数据。

每个测试数据的第一行为一个整数N,表示河蟹词典的大小。

接下来的N行,每一行为一个由小写英文字母组成的河蟹词语。

接下来的一行,为一篇长度不超过M,由小写英文字母组成的文章。

对于60%的数据,所有河蟹词语的长度总和小于10, M<=10

对于80%的数据,所有河蟹词语的长度总和小于10^3, M<=10^3

对于100%的数据,所有河蟹词语的长度总和小于10^6, M<=10^6, N<=1000

输出

对于每组测试数据,输出一行"YES"或者"NO",表示文章中是否含有河蟹词语。

样例输入
6
aaabc
aaac
abcc
ac
bcd
cd
aaaaaaaaaaabaaadaaac
样例输出
YES
  1 import java.util.ArrayList;
  2 import java.util.Scanner;
  3
  4
  5 public class Main {
  6
  7     public static void main(String argv []){
  8         Scanner in = new Scanner(System.in);
  9         int n=Integer.parseInt(in.nextLine());
 10         String name = "Dic"; boolean b=false;
 11         root m = new root(name);
 12         for(int i=0; i<n; i++){
 13             m.add(in.nextLine(), m);
 14         }
 15         String x=in.nextLine();
 16         in.close();
 17         //m.scan(m);
 18         for(int j=0; j<x.length(); j++){
 19             String s=x.substring(j, x.length());
 20             //System.out.println(s);
 21             b=m.checkContain(m, s);
 22             if(b==true)
 23             { break;}
 24
 25         }
 26         if(b==true)
 27             System.out.println("YES");
 28         else
 29             System.out.println("NO");
 30
 31     }
 32
 33 }
 34
 35     class Trie{
 36         int  y;
 37         char k;
 38         ArrayList<Trie> list=new ArrayList<Trie>();
 39         public Trie(int y, char k){
 40             this.y=y;
 41             this.k=k;
 42         }
 43         public void add(Trie t){
 44             this.list.add(t);
 45         }
 46         public void set(int y){
 47             this.y=y;
 48         }
 49         public Trie addChar(Trie t, char c){
 50
 51             Trie N = new Trie(0,c);
 52             int yes=0;
 53             for(int j=0; j<t.list.size();j++){
 54                 if(t.list.get(j).k==c)
 55                     {N=t.list.get(j); yes=1; break; }
 56         }
 57             if(yes==0)
 58             {t.list.add(N);}
 59             return N;
 60     }
 61
 62         public void scanTrie(Trie t){
 63
 64             System.out.println(" "+t.k+t.y);
 65             for(int u=0; u<t.list.size(); u++){
 66                 scanTrie(t.list.get(u));
 67             }
 68
 69         }
 70
 71         public Trie containC(Trie t, char c){
 72
 73             for(int j=0; j<t.list.size(); j++){
 74
 75                 if(t.list.get(j).k==c)
 76                     return t.list.get(j);
 77             }
 78             return null;
 79
 80         }
 81 }
 82
 83
 84     class root{
 85         String name;
 86         ArrayList<Trie> list=new ArrayList<Trie>();
 87         public root(String name){
 88             this.name=name;
 89         }
 90         public void add(String s, root t){
 91             char[] k = s.toCharArray();
 92             Trie N = new Trie(0,k[0]); int yes=0;
 93             for(int j=0; j<t.list.size();j++){
 94                 if(t.list.get(j).k==k[0])
 95                     {N=t.list.get(j); yes=1; break;}
 96
 97             }
 98             if(yes==0){
 99                         t.list.add(N);
100                 }
101             for(int i=1; i<k.length; i++){
102                 N=N.addChar(N, k[i]);
103             }
104             N.set(1);
105         }
106         public void scan(root t){
107
108             for(int i=0; i<t.list.size(); i++){
109                 t.list.get(i).scanTrie(t.list.get(i));
110             }
111
112
113         }
114         public boolean checkHas(root t,String k){
115
116             char[] c =k.toCharArray(); int yes=0;
117             Trie N = new Trie(0,‘0‘);
118             for(int j=0; j<t.list.size(); j++){
119                 if(t.list.get(j).k==c[0])
120                     {N=t.list.get(j); yes=1; break;}
121             }
122             if(yes==0)
123                 return false;
124             for(int i=1; i<c.length; i++){
125                 N=N.containC(N, c[i]);
126                 if(N==null)
127                     return false;
128             }
129             if(N.y==1)
130              return true;
131             else
132              return false;
133         }
134       public boolean checkContain(root t,String k){
135
136             char[] c =k.toCharArray(); int yes=0;
137             Trie N = new Trie(0,‘0‘);
138             for(int j=0; j<t.list.size(); j++){
139                 if(t.list.get(j).k==c[0])
140                     {N=t.list.get(j); yes=1; break;}
141             }
142             if(yes==0)
143                 return false;
144             for(int i=1; i<c.length; i++){
145                 N=N.containC(N, c[i]);
146                 if(N==null)
147                     return false;
148                 if(N.y==1)
149                     return true;
150             }
151             if(N.y==1)
152              return true;
153             else
154              return false;
155         }
156     }

hiho结果:

时间: 2024-08-02 21:35:44

Trie 图的相关文章

hiho一下 第二周&amp;第四周:从Trie树到Trie图

hihocoder #1014 题目地址:http://hihocoder.com/problemset/problem/1014 hihocoder #1036 题目地址: http://hihocoder.com/problemset/problem/1036 trie图其实就是trie树+KMP #1014trie树 #include<stdio.h> #include <algorithm> #include <cstring> #include <str

Trie图 &amp; AC自动机初学(1)

题目来源于:Hihocoder 时间限制:20000ms 单点时限:1000ms 内存限制:512MB 描述 前情回顾 上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从互联网上收集来的文章,和一本厚厚的河蟹词典,而他们要做的是判断这篇文章中是否存在那些属于河蟹词典中的词语. 当时,小Hi和小Ho的水平还是十分有限,他们只能够想到:"枚举每一个单词,然后枚举文章中可能的起始位置,然后进行匹配,看能否成功."这样非常朴素的想法,但是这样的算法时间复杂度是

Trie图

DFA 确定性有限状态自动机 DFA确定性有限状态自动机是一种图结构的数据结构,可以由(Q, q0, A, Sigma, Delta)来描述,其中Q为状态集,q0为初始状态,A为终态集合,Sigma为字母表,Delta为转移函数.它表示从唯一一个起始状态q0开始,经过有限步的Delta转移,转移是根据字母表Sigma中的元素来进行,最终到达终态集合A中的某个状态的状态移动.  如图所示是一个终态集合为{"nano"}的DFA.     DFA只能有一个起点而可以有多个终点.每个节点都有

hihoCoder#1036 Trie图

原题地址 看了这篇博文,总算是把Trie图弄明白了 Runtime Error了无数次,一直不知道为什么,于是写了个脚本生成了一组大数据,发现果然段错误了. 调试了一下午,总算闹明白了,为什么呢? 1. 空间超大的变量不要放在函数里,会爆栈,应该弄成全局变量或者是从堆上动态分配. 2. 看清题目的数据范围,一开始我的MAX_NODE设的是1024... 代码: 1 #include <iostream> 2 #include <cstring> 3 4 using namespac

hdu2457 Trie图+dp

hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Trie图中的结点)所需要修改的最少次数 那么dp[0->n][0->size] = INF ,  dp[0][root] = 0,  n代表字符串长度, size代表状态数 那么答案就是  min{dp[n][size]} 我们根据模式串所建的Trie图, 进行模拟构造不包含模式串的字符串 从第一个

【hihoCoder】1036 Trie图

题目:http://hihocoder.com/problemset/problem/1036 给一个词典dict,词典中包含了一些单词words.要求判断给定的一个文本串text中是否包含这个字典中的单词words. 相关基础的理解 1. 与用KMP解决的问题的差别 KMP:输入原串S和一个模式串T,判断T是否出现在S中.通过对T计算next数组,避免原串S的回溯. 现在的问题:输入文本串text和多个单词words,判断words中是否有出现在text中.同样希望输入的text不用进行回溯.

hihocoder 1036 Trie图(AC自动机)

传送门 Description 上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从互联网上收集来的文章,和一本厚厚的河蟹词典,而他们要做的是判断这篇文章中是否存在那些属于河蟹词典中的词语. 当时,小Hi和小Ho的水平还是十分有限,他们只能够想到:“枚举每一个单词,然后枚举文章中可能的起始位置,然后进行匹配,看能否成功.”这样非常朴素的想法,但是这样的算法时间复杂度是相当高的,如果说词典的词语数量为N,每个词语长度为L,文章的长度为M,那么需要进行的计算次数是在N

[hiho 04]Trie图

题目描述 Trie 图就是在 Trie 树上建立 fail 指针,类似于KMP算法中的next数组的作用. 这个数据结构的作用是判断一个字符串中是否包含一组字符串中的任意一个. 结构体定义是这样的: typedef struct trie_node { trie_node *nodes[26]; trie_node *fail = NULL; bool word_end = false; trie_node() { for (int i = 0; i < 26; i++) { nodes[i]

【Trie图】BZOJ3940-[Usaco2015 Feb]Censoring

[题目大意] 有一个匹配串和多个模式串,现在不断删去匹配串中的模式串,求出最后匹配串剩下的部分. [思路] 众所周知,KMP的题往往对应着一道AC自动机quq.本题同BZOJ3942(KMP),这里改成AC自动机即可. 我一开始写了原始的AC自动机,写挂了.后来思考了一下,应当用Trie图,机智地1A. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm&

poj_3987 Trie图

题目大意 有N个病毒,病毒由A-Z字母构成,N个病毒各不相同.给出一段程序P,由A-Z字母构成,若病毒在在程序P或者P的逆转字符串P'中存在,则该程序P被该病毒感染.求出程序P被多少种病毒感染. 题目分析 典型的多模式串的字符串匹配问题,考虑使用Trie图.将M个待查的字符串作为模式串插入Trie图中,然后设置前缀指针,构造DFA.     判断程序P字符串翻转之后,是否含有某个模式串,一种方法是将P翻转,然后在DFA上查找:另一种是在构造DFA的时候,将模式串翻转,然后插入Trie图中,在匹配