题目:http://ybt.ssoier.cn:8088/problem_show.php?pid=1479

题目思路:一道AC自动机的模板题

备注:还不会字典树和KMP的尽早回去重修

如果让你在一篇文章里找一个单词是否出现,你会怎么做?

爆了他吗?可以,不过KMP比较省时间

然后就有人提出来了“找一堆单词”的想法
爆了他?怎么爆?弄数组吗?但是把单词存进数组里很麻烦

于是,字典树应运而生,把单词搞进树里,操作简单省时间,公共的前缀又将空间复杂度简化,实属不可多得

但这还不够,时间复杂度依旧未曾整理好,每一个单词的成功搜索都代表着新一轮字典树的重修,从根再向上爬实在是费时费力

于是有人提出了这么一个问题:能不能把字典树和KMP有机结合起来?

当我把一个单词搜干净或是确定它搜不成的时候,我就可以不往根去,而是去找另一个节点玩

那这另一个节点,我要保证他再搜索那个单词的时候就已经把这个节点和这个节点之前连着的东西找干净了,如下:

A B B H F V U D N H G B H G Y D

H G B H G Y D

所以你发现了什么吗?从根到这个节点组成的字符串,是我搜索的那个单词的后缀

那么靠着这个,我们可以得出2条原则:

1.这个单词结束后,我去的节点的深度一定不比我结束时所在的节点深,也就是我去的节点到根的距离小于等于这个节点到根的距离

不然就会出现“一个长为5的单词是一个长为4的单词的后缀”,这是什么J*B玩意

2.在保证1的情况下,这个深度要尽量的大,越大越省事

有了这两条原则,我们就可以愉快地写数组了

重新声明,Fail[i] 存的是当这个单词在 i 处结束(或是匹配成功或是失配)它会转移到 Fail[i] ,以后简称为 j 。

开始搜索:

首先,从根上分下来的第一个东西的 j 一定会往根上跑

一是它不可能有数值相同的对家,二是比它深度小的也就只有根了

然后对于后面有着两重可能

一是如果我们当前搜索的节点有儿子,那么 Fail[儿子] 就指向 Fail[它爹] 的节点与儿子数值相同的儿子

然后把这个儿子存起来,当做以后我们要搜索的节点

就算那个“数值相同的儿子”是 0 也没关系,我们让它当跳板,再往回跑就可以了

二是如果没有,就把 Fail[它爹] 的节点的数值相同的儿子指过去就可以

这个“指过去”充当了一个跳板的作用,毕竟还有一堆节点找“它爹”呢,没个儿子给他们指也就说不过去

自此,全部清零

最后一个问题:怎么计数

给每个单词的末尾加个1就可以,碰上了就加一下。

#include<bits/stdc++.h>
using namespace std;
int T,n,tot=0;
string s;
int trie[500010][26],answ[100010],nexT[500010];
//树,单词末尾计数和fail(这里起名字用的是nexT)
void putin(string s){
int p=0,l=s.size();
for(int i=0;i<l;++i){
int id=s[i]-'a';
if(!trie[p][id]){
trie[p][id]=++tot;
}
p=trie[p][id];
}//家常便饭的字典树
answ[p]++;
//计数
}
void hnext(){
queue<int> q;
//由于节点过多,整个队列会方便些
//对第一层特殊处理
for(int i=0;i<26;++i){
int g=trie[0][i];//一个一个来
if(g){//如果有东西
nexT[g]=0;//往回指根
q.push(g);//存起来
}
//如果没有就不用管了
}
while(!q.empty()){//保证有节点可搜
int t=q.front();//找到
q.pop();//扔掉
for(int i=0;i<26;++i){
int g=trie[t][i];
//g是当前节点的儿子
if(g){//有东西
q.push(g);//存起来
nexT[g]=trie[nexT[t]][i];
//儿子指儿子
}
else{//如果没有
trie[t][i]=trie[nexT[t]][i];
//也要指一下当跳板
//但不要再存了
}
}
}
}
int found(string s){
int ans=0,p=0,l=s.size();
for(int i=0;i<l;++i){
int id=s[i]-'a';
p=trie[p][id];
for(int j=p;j&&answ[j]!=-1;j=nexT[j]){
//适配或失配则回去
ans=ans+answ[j];
//慢慢加
answ[j]=-1;
//搜到了就不要再搜了
}
}
return ans;
}
int main(){
scanf("%d",&T);
while(T--){
memset(trie,0,sizeof(trie));
memset(answ,0,sizeof(answ));
memset(nexT,0,sizeof(nexT));
tot=0;
scanf("%d",&n);
for(int i=1;i<=n;++i){
cin>>s;
putin(s);
}
hnext();
cin>>s;
printf("%d\n",found(s));
}
return 0;
}

021(Keywords Search)(AC自动机)的更多相关文章

  1. 【HDU2222】Keywords Search AC自动机

    [HDU2222]Keywords Search Problem Description In the modern time, Search engine came into the life of ...

  2. hdu2222 Keywords Search ac自动机

    地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=2222 题目: Keywords Search Time Limit: 2000/1000 MS ...

  3. HDU2222 Keywords Search [AC自动机模板]

    Keywords Search Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others ...

  4. Keywords Search(AC自动机模板)

    Keywords Search Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others ...

  5. HDU2222 Keywords Search —— AC自动机

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222 Keywords Search Time Limit: 2000/1000 MS (Java/O ...

  6. Keywords Search AC自动机

    In the modern time, Search engine came into the life of everybody like Google, Baidu, etc. Wiskey al ...

  7. Match:Keywords Search(AC自动机模板)(HDU 2222)

    多模匹配 题目大意:给定很多个字串A,B,C,D,E....,然后再给你目标串str字串,看目标串中出现多少个给定的字串. 经典AC自动机模板题,不多说. #include <iostream& ...

  8. HDU 2222 Keywords Search(AC自动机模板题)

    学习AC自动机请戳这里:大神blog........ 自动机的模板: #include <iostream> #include <algorithm> #include < ...

  9. hdu 2222 Keywords Search ac自动机入门

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222 题意:有N(N <= 10000)个长度不超过50的模式串和一个长度不超过1e6的文本串. ...

  10. HDU 2222 Keywords Search (AC自动机)

    题意:就是求目标串中出现了几个模式串. 思路:用int型的end数组记录出现,AC自动机即可. #include<iostream> #include<cstdio> #inc ...

随机推荐

  1. APL 和 Web APL 的概述

    APL APl ( Application ProgrammingInterface,应用程序编程接口) 是一些预先定义的函数,目的是提供应用程序 与开发人员基于某软件或硬件得以访问一组例程的能力,而 ...

  2. v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码

    本篇关键词:指令格式.条件域.类型域.操作域.数据指令.访存指令.跳转指令.SVC(软件中断) 内核汇编相关篇为: v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的 v75.03 ...

  3. Unity制作一个小星球

    制作过程 在场景中新建一个球体(Planet)和一个胶囊(Player),适当缩放并添加材质,这里胶囊会被视为玩家 然后将摄像机设为胶囊(Player)的子物体 自行调整合适的摄像机视角 新建脚本Gr ...

  4. RedirectAttributes重定向

    1.url显示参数信息(不安全) @Controller @RequestMapping("/UserOperate") public class UserController { ...

  5. [笔记] prufer 序列

    什么是 prufer 序列 是可以和 \(n\) 个有标号节点的无根树一一对应的长度为 \(n-2\) 的序列. 一般来说是用于和树相关的组合计数问题,但是可能会出现一些变形,所以除了要了解一些性质, ...

  6. 万字长文详解HBase读写性能优化

    一.HBase 读优化 1. HBase客户端优化 和大多数系统一样,客户端作为业务读写的入口,姿势使用不正确通常会导致本业务读延迟较高实际上存在一些使用姿势的推荐用法,这里一般需要关注四个问题: 1 ...

  7. ElasticSearch7.3学习(二十三)----RestHighLevelClient Java api实现match_all、ids、match、term、multi_match、bool、filter、sort等不同的搜索方式

    1.数据准备 首先创建book索引 PUT /book/ { "settings": { "number_of_shards": 1, "number ...

  8. vue3 迫不得已我硬着头皮查看了keepalive的源代码,解决了线上的问题

    1.通过本文可以了解到vue3 keepalive功能 2.通过本文可以了解到vue3 keepalive使用场景 3.通过本文可以学习到vue3 keepalive真实的使用过程 4.通过本文可以学 ...

  9. Docker的网络

    概述 docker使用Linux桥接网卡,在宿主机虚拟一个docker容器网桥(docker0),docker启动一个容器时会根 据docker网桥的网段分配给容器一个IP地址,称为Container ...

  10. 【zigbee无线通信模块步步详解】ZigBee3.0模块建立远程网络控制方法

    本文以路灯控制应用为例,简述ZigBee3.0模块使用流程. 一.建立网络 1.通过USB转串口模块将出厂的ZigBee自组网模块连接,打开上位机软件"E180-ZG120A-Setting ...