时间限制: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
 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 图的更多相关文章

  1. 【BZOJ-2938】病毒 Trie图 + 拓扑排序

    2938: [Poi2000]病毒 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 609  Solved: 318[Submit][Status][Di ...

  2. 【hihoCoder】1036 Trie图

    题目:http://hihocoder.com/problemset/problem/1036 给一个词典dict,词典中包含了一些单词words.要求判断给定的一个文本串text中是否包含这个字典中 ...

  3. 【hihoCoder 1036】Trie图

    看了一下简单的$Trie图$,调模板调啊调一连调了$2h$,最后发现$-'a'$打成$-'A'$了hhh,有种摔键盘的冲动. $Trie图$是$Trie树$上建立“前缀边”,不用再像在$Trie树$上 ...

  4. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  5. Trie图和Fail树

    Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造ne ...

  6. hdu2457 Trie图+dp

    hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Tr ...

  7. Trie图

    AC自动机是KMP的多串形式,当文本串失配时,AC自动机的fail指针告诉我们应该跳到哪里去继续匹配(跳到当前匹配串的最长后缀去),所以AC自动机的状态是有限的 但是AC自动机具有不确定性, 比如要求 ...

  8. CF 291E. Tree-String Problem [dfs kmp trie图优化]

    CF291E 题意:一棵树,每条边上有一些字符,求目标串出现了多少次 直接求目标串的fail然后一边dfs一边跑kmp 然后就被特殊数据卡到\(O(n^2)\)了... 因为这样kmp复杂度分析的基础 ...

  9. AC自动机相关Fail树和Trie图相关基础知识

    装载自55242字符串AC自动机专栏 fail树 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账- 有了这个东西, ...

  10. AC自动机学习笔记-2(Trie图&&last优化)

    我是连月更都做不到的蒟蒻博主QwQ 考虑到我太菜了,考完noip就要退役了,所以我决定还是把博客的倒数第二篇博客给写了,也算是填了一个坑吧.(最后一篇?当然是悲怆のnoip退役记啦QAQ) 所以我们今 ...

随机推荐

  1. Python模块学习 - Argparse

    argparse模块 在Python中,argparse模块是标准库中用来解析命令行参数的模块,用来替代已经过时的optparse模块.argparse模块能够根据程序中的定义从sys.argv中解析 ...

  2. xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance(xsi:schemaLocation详解)

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"中xsi的意思是 :本xml文件中要用到某些来自xsi代表的“http:/ ...

  3. C基础 读写锁中级剖析

    引言 读写锁 是为了 解决, 大量 ''读'' 和 少量 ''写'' 的业务而设计的. 读写锁有3个特征: 1.当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞 2.当 ...

  4. KettleDB连接jdbc连接池参数介绍

    http://sheng8407-sina-com.iteye.com/blog/1163245 http://blog.csdn.net/dingxingmei/article/details/41 ...

  5. [ Python ] 基本数据类型及属性(下篇)

    1. 基本数据类型 (1) list 列表     (2) tuple 元组     (3) dict 字典     (4) set 集合 2. list 列表方法 Python 内置的一种数据类型, ...

  6. php性能的问题

    一.影响php性能的常见原因 1.php自身语法使用不当 2.php做了不擅长的时期() 3.php的周边环境(服务器Linux,磁盘:文件存储,数据库,缓存:内存,网络:带宽) 4.php自身的短板 ...

  7. _stdcall调用

    以前看windows编程时一直有个 _stdcall 函数调用约定 一直不是很理解,只能硬记. 现在终于在<程序是怎样跑起来的>这本书书中找到了答案. 1. _stdcall 是stand ...

  8. redis之(十)redis实现消息中间件的功能

    [一]任务队列的好处 --->松耦合:生产者和消费者无需知道彼此实现的细节,只需要约定好任务的描述格式.这使得生产者和消费者可以由不同的团队使用不同的编程语言编写. --->易于扩展:消费 ...

  9. centos7.5&ubuntu18.10安装Google浏览器

    一.手动安装 1. 下载 rpm 包https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm 2. 安装依赖 ...

  10. codeforces 360 B

    B - Levko and Array 题目大意:给你你个长度为n的数列a,你最多改变k个值,max{ abs ( a[ i + 1] - a[ i ] ) } 的最小值为多少. 思路:这个题很难想到 ...