Trie 图
描述
前情回顾
上回说到,小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想了想说道。
“现在我们有了一个时间复杂度在O(ML)级别的方法,但是我们的征途在星辰大海,啊不,我们不能满足于这样一个60分的方法。所以呢,我们还是要贯彻我们一贯的做法,寻找在这个算法中那些冗余的计算!“小Hi道:”那么我们现在来看看Trie树进行计算的时候都发生了些什么。”
“那么现在……”小Hi刚要开口,就被小Ho无情打断。
“可是小Hi老师~你看在这种情况下,结点C找不到对应的后缀结点,它对应的路径是aaabc,而aabc在Trie里面是走不出来的!”小Ho手中挥舞着一张纸,问道。
“你个瓜娃子,老是拆老子台做啥子!……阿不,小Ho你别担心,我这就要讲解如何求后缀结点呢~”小Hi笑容满面的说道。
“原来如此!这样我就知道了每一个结点的后缀结点了,接下来我就可以很轻松的解决河蟹先生交给我的问题了呢!”小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
import java.util.ArrayList;
import java.util.Scanner; public class Main { public static void main(String argv []){
Scanner in = new Scanner(System.in);
int n=Integer.parseInt(in.nextLine());
String name = "Dic"; boolean b=false;
root m = new root(name);
for(int i=0; i<n; i++){
m.add(in.nextLine(), m);
}
String x=in.nextLine();
in.close();
//m.scan(m);
for(int j=0; j<x.length(); j++){
String s=x.substring(j, x.length());
//System.out.println(s);
b=m.checkContain(m, s);
if(b==true)
{ break;} }
if(b==true)
System.out.println("YES");
else
System.out.println("NO"); } } class Trie{
int y;
char k;
ArrayList<Trie> list=new ArrayList<Trie>();
public Trie(int y, char k){
this.y=y;
this.k=k;
}
public void add(Trie t){
this.list.add(t);
}
public void set(int y){
this.y=y;
}
public Trie addChar(Trie t, char c){ Trie N = new Trie(0,c);
int yes=0;
for(int j=0; j<t.list.size();j++){
if(t.list.get(j).k==c)
{N=t.list.get(j); yes=1; break; }
}
if(yes==0)
{t.list.add(N);}
return N;
} public void scanTrie(Trie t){ System.out.println(" "+t.k+t.y);
for(int u=0; u<t.list.size(); u++){
scanTrie(t.list.get(u));
} } public Trie containC(Trie t, char c){ for(int j=0; j<t.list.size(); j++){ if(t.list.get(j).k==c)
return t.list.get(j);
}
return null; }
} class root{
String name;
ArrayList<Trie> list=new ArrayList<Trie>();
public root(String name){
this.name=name;
}
public void add(String s, root t){
char[] k = s.toCharArray();
Trie N = new Trie(0,k[0]); int yes=0;
for(int j=0; j<t.list.size();j++){
if(t.list.get(j).k==k[0])
{N=t.list.get(j); yes=1; break;} }
if(yes==0){
t.list.add(N);
}
for(int i=1; i<k.length; i++){
N=N.addChar(N, k[i]);
}
N.set(1);
}
public void scan(root t){ for(int i=0; i<t.list.size(); i++){
t.list.get(i).scanTrie(t.list.get(i));
} }
public boolean checkHas(root t,String k){ char[] c =k.toCharArray(); int yes=0;
Trie N = new Trie(0,'0');
for(int j=0; j<t.list.size(); j++){
if(t.list.get(j).k==c[0])
{N=t.list.get(j); yes=1; break;}
}
if(yes==0)
return false;
for(int i=1; i<c.length; i++){
N=N.containC(N, c[i]);
if(N==null)
return false;
}
if(N.y==1)
return true;
else
return false;
}
public boolean checkContain(root t,String k){ char[] c =k.toCharArray(); int yes=0;
Trie N = new Trie(0,'0');
for(int j=0; j<t.list.size(); j++){
if(t.list.get(j).k==c[0])
{N=t.list.get(j); yes=1; break;}
}
if(yes==0)
return false;
for(int i=1; i<c.length; i++){
N=N.containC(N, c[i]);
if(N==null)
return false;
if(N.y==1)
return true;
}
if(N.y==1)
return true;
else
return false;
}
}hiho结果:
Trie 图的更多相关文章
- 【BZOJ-2938】病毒 Trie图 + 拓扑排序
2938: [Poi2000]病毒 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 609 Solved: 318[Submit][Status][Di ...
- 【hihoCoder】1036 Trie图
题目:http://hihocoder.com/problemset/problem/1036 给一个词典dict,词典中包含了一些单词words.要求判断给定的一个文本串text中是否包含这个字典中 ...
- 【hihoCoder 1036】Trie图
看了一下简单的$Trie图$,调模板调啊调一连调了$2h$,最后发现$-'a'$打成$-'A'$了hhh,有种摔键盘的冲动. $Trie图$是$Trie树$上建立“前缀边”,不用再像在$Trie树$上 ...
- 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组
涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...
- Trie图和Fail树
Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造ne ...
- hdu2457 Trie图+dp
hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Tr ...
- Trie图
AC自动机是KMP的多串形式,当文本串失配时,AC自动机的fail指针告诉我们应该跳到哪里去继续匹配(跳到当前匹配串的最长后缀去),所以AC自动机的状态是有限的 但是AC自动机具有不确定性, 比如要求 ...
- CF 291E. Tree-String Problem [dfs kmp trie图优化]
CF291E 题意:一棵树,每条边上有一些字符,求目标串出现了多少次 直接求目标串的fail然后一边dfs一边跑kmp 然后就被特殊数据卡到\(O(n^2)\)了... 因为这样kmp复杂度分析的基础 ...
- AC自动机相关Fail树和Trie图相关基础知识
装载自55242字符串AC自动机专栏 fail树 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账- 有了这个东西, ...
- AC自动机学习笔记-2(Trie图&&last优化)
我是连月更都做不到的蒟蒻博主QwQ 考虑到我太菜了,考完noip就要退役了,所以我决定还是把博客的倒数第二篇博客给写了,也算是填了一个坑吧.(最后一篇?当然是悲怆のnoip退役记啦QAQ) 所以我们今 ...
随机推荐
- [Leetcode Week12]Unique Paths
Unique Paths 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/unique-paths/description/ Description A ...
- 1000: 恶意IP 课程作业
1000: 恶意IP Time Limit: 1 Sec Memory Limit: 16 MB Description Water同学最近好不容易学会了用Tornado建起一个个人的Website ...
- 关闭自动弹出照片自动弹出iTunes以及关闭手机照片流
关闭自动弹出照片自动弹出iTunes以及关闭手机照片流 如何阻止iPhone连接Mac后自动弹出照片? 时间:2015/6/18 17:07:15来源:本站原创作者:Chenjh我要评论 很多新 iP ...
- grep常见操作整理(更新)
提取邮箱和URL [root@test88 ~]# cat url_email.txt root@gmail.com,http://blog.peter.com,peter@qq.com [root@ ...
- php各个版本的区别
一. PHP 5.2.5.3.5.4.5.5.5.6 版本区别对比以及新功能详解 PHP5.2 以前:autoload, PDO 和 MySQLi, 类型约束 PHP5.2:JSON 支持 PHP5. ...
- java的collection集合
# 原创,转载请先留言 1.集合的由来 数组的长度是固定的,当需要增加或减少元素时需要对数组重新定义,太麻烦了.java内部给我们提供了集合类,能存储任意对象,长度可以改变的,随着元素的增加而增加,随 ...
- maven设置打jar包并引入依赖包
--------------------------------------------------------方法一:将jar包和项目打在一起---------------------------- ...
- jps命令学习
jps命令学习 标签(空格分隔): jvm jps介绍 ( JVM Process Status Tool ) 网文 jps命令用于查看当前Java进程及其pid等相关信息,同ps -ef | gre ...
- AC日记——「SCOI2016」美味 LiBreOJ 2016
#2016. 「SCOI2016」美味 思路: 主席树: 代码: #include <bits/stdc++.h> using namespace std; #define maxa 26 ...
- AC日记——自然数和分解 codevs 2549
自然数和分解 思路: 水题: 代码: #include <bits/stdc++.h> using namespace std; ][]; int main() { cin>> ...