代码:

#include<iostream>
#include<vector>
#include<cstdio>
#include<queue>
#include<map>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
#include<cstring>
using namespace std;
typedef long long ll;
const ll INF=;
const int MAXN=;
int trie[MAXN][]; //i到j的编号
int val[MAXN]; //代表这个节点有多少单词
int fail[MAXN]; //fail指针,意为失配时去的位置
int cnt;
queue<int> q;
void ins(string str){ //添加单词
int last=; //代表着多次失配之后最后跳到的地方
for(int i=;i<(int)str.length();i++){
int v=str[i]-'a';
if(!trie[last][v]) trie[last][v]=++cnt; //如果没有对应的这个节点的话,就加上(标记)
last=trie[last][v]; //将上次的编号记住
}
val[last]++; //单词数+1
}
void build(){
for(int i=;i<;i++) //遍历与根节点连接的点
if(trie[][i]){ //如果有这个字母的儿子
fail[trie[][i]]=; //将fail指针指向根
q.push(trie[][i]); //加入搜索队列 }
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=;i<;i++){ //枚举其每个儿子
if(trie[u][i]){ //如果有这个节点
fail[trie[u][i]]=trie[fail[u]][i]; //fail指针指向父节点(当前节点)的fail指针指向的节点的相同字母节点
q.push(trie[u][i]); //加入这个点
}
else{
trie[u][i]=trie[fail[u]][i]; //没有这个点的话将其等同于父节点(当前节点)的fail指针的相同字母节点
}
}
}
}
int query(string str){
int last=,ans=;
for(int i=;i<(int)str.length();i++){
last=trie[last][str[i]-'a']; //获得当前字母,当前位置的编号
for(int j=last;j&&~val[j];j=fail[j]) { //只要没有结尾,就按照fail路线走
ans+=val[j]; //加上以这个节点结尾的单词书亮
val[j]=-; //已经拿走了,所以没有了
}
}
return ans;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie();
int n;
cin>>n;
string st;
for(int i=;i<n;i++){
cin>>st;
ins(st); //添加单词
}
build(); //初始化
cin>>st;
cout<<query(st); //输出 return ;
}

查看神奇代码

0.容器

大部分人是用struct或者class实现内部的函数,但是作为考场上最倩的仔(雾,我决定使用数组存。

(很不理解为什么要用struct存,OI又不是写工程)

int trie[MAXN][27];     //代表从前、后的编号
int val[MAXN]; //代表这个节点有多少单词
int fail[MAXN]; //fail指针,意为失配时去的位置
int cnt; //当前编号

MAXN是数据的范围,是一个常量

1.插入

插入相当于从根一直走到最后一个字母的位置,没有就插入并赋予它一个新编号

void ins(string str){                                  //添加单词
int last=0; //代表着多次失配之后最后跳到的地方
for(int i=0;i<(int)str.length();i++){
int v=str[i]-'a';
if(!trie[last][v]) trie[last][v]=++cnt; //如果没有对应的这个节点的话,就加上(标记为新的节点,赋予其新编号)
last=trie[last][v]; //将这次的编号记住,以便下次使用
}
val[last]++; //单词数+1
}

 

2.Build

Build的是Fail指针:

先把所有与根节点连接的点的Fail指针指向根,然后将它们加入队列

然后BFS整棵树:

取出队首,然后将其儿子的Fail指针指向父亲的fail指针指向的相同字母的节点

赋予队首不存在的子节点父亲节点的Fail指针指向的相同字母的节点的编号

void build(){
for(int i=0;i<26;i++) //遍历与根节点连接的点
if(trie[0][i]){ //如果有这个字母的儿子
fail[trie[0][i]]=0; //将fail指针指向根
q.push(trie[0][i]); //加入搜索队列 }
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){ //枚举其每个儿子
if(trie[u][i]){ //如果有这个节点
fail[trie[u][i]]=trie[fail[u]][i]; //fail指针指向父节点(当前节点)的fail指针指向的节点的相同字母节点
q.push(trie[u][i]); //加入这个点
}
else{
trie[u][i]=trie[fail[u]][i]; //没有这个点的话将其等同于父节点(当前节点)的fail指针的相同字母节点
}
}
}
}
int query(string str){
int last=0,ans=0;
for(int i=0;i<(int)str.length();i++){
last=trie[last][str[i]-'a']; //获得当前字母,当前位置的编号
for(int j=last;j&&~val[j];j=fail[j]) { //只要没有结尾,就按照fail路线走
ans+=val[j]; //加上以这个节点结尾的单词书亮
val[j]=-1; //已经拿走了,所以没有了
}
}
return ans;
}

  

3.顺序

输入单词

ins

build

输出query

int n;
cin>>n;
string st;
for(int i=0;i<n;i++){
cin>>st;
ins(st); //添加单词
}
build(); //初始化
cin>>st;
cout<<query(st); //输出

  

本模版可以在洛谷P3808 https://www.luogu.org/problemnew/show/P3808提交,已通过

【字符串处理】AC自动机知识点&代码的更多相关文章

  1. 字符串处理-AC自动机

    估计在OJ上刷过题的都会对AC自动机这个名词很感兴趣,同样,记得去年ACM暑期集训的时候,在最后讲到字符串部分,听说了这个算法的名字之后就对于它心向往之,AC正好是Accept的简称,字面意义上的理解 ...

  2. HDU-2222 Keywords Search 字符串问题 AC自动机

    题目链接:https://cn.vjudge.net/problem/HDU-2222 题意 给一些关键词,和一个待查询的字符串 问这个字符串里包含多少种关键词 思路 AC自动机模版题咯 注意一般情况 ...

  3. 字符串(AC自动机):HDU 5129 Yong Zheng's Death

    Yong Zheng's Death Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/O ...

  4. 2017ACM暑期多校联合训练 - Team 8 1006 HDU 6138 Fleet of the Eternal Throne (字符串处理 AC自动机)

    题目链接 Problem Description The Eternal Fleet was built many centuries ago before the time of Valkorion ...

  5. HDU-2896 病毒侵袭 字符串问题 AC自动机

    题目链接:https://cn.vjudge.net/problem/HDU-2896 题意 中文题 给一些关键词和一个字符串,问字符串里包括了那几种关键词 思路 直接套模版 改insert方法,维护 ...

  6. HDU-3065 病毒侵袭持续中 字符串问题 AC自动机

    题目链接:https://cn.vjudge.net/problem/HDU-3065 题意 跟上一道题是几乎一模一样,这次是统计关键词的出现次数 一个相当坑的地方,注意多组样例 思路 套模版 改in ...

  7. 字符串(AC自动机):COCI 2015 round 5 divljak

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAy0AAANaCAIAAAALVTQoAAAgAElEQVR4nOy9X2hbx773PXfrQgQjDq

  8. 字符串:AC自动机

    给出一个字典和一个模式串,问模式串中出现几个字典中的单词 最后一行是大串,之前输入的是小串 #include<iostream> #include<cstdio> using ...

  9. 多模字符串匹配算法之AC自动机—原理与实现

    简介: 本文是博主自身对AC自动机的原理的一些理解和看法,主要以举例的方式讲解,同时又配以相应的图片.代码实现部分也予以明确的注释,希望给大家不一样的感受.AC自动机主要用于多模式字符串的匹配,本质上 ...

随机推荐

  1. git——merge和rebase的区别

    参考http://www.jianshu.com/p/129e721adc6e 我在公司里看到其他同事都使用git pull --rebase拉取远程代码,而我总是用git pull,也有同事和我说过 ...

  2. ZUK Z2 Pro(Z2121) 免解锁BL 免rec Magisk Xposed ROOT 救砖 ZUI 4.0.247

    >>>重点介绍<<< 第一:本刷机包可卡刷可线刷,刷机包比较大的原因是采用同时兼容卡刷和线刷的格式,所以比较大第二:[卡刷方法]卡刷不要解压刷机包,直接传入手机后用 ...

  3. Leetcode0143--Reorder List 链表重排

    [转载请注明]https://www.cnblogs.com/igoslly/p/9351564.html 具体的图示可查看 链接 代码一 /** * Definition for singly-li ...

  4. Android开发笔记(9)——初步设置Menu

    转载请注明:http://www.cnblogs.com/igoslly/p/6858656.html   初步设置Menu   设置Menu,在ActionBar上添加按钮操作:         在 ...

  5. 使用Kettle增量抽取MongoDB数据实践

    需求: 增量抽取MongoDB数据并加载到MSSQL 由于不能使用关系型数据库的自定义SQL, 所以主要遇到的问题有: 增量时间的查询和参数控制 ETL的批次信息和调用参数的写入 第一个问题的解决如下 ...

  6. css图片高清适配

    同一张图片,在普通屏显示正常,但高清屏出现模糊.原因是原来一个像素的点分成的四个像素的点进行了显示. 解决方案:在高清屏中把图片变成二倍图,前提是二倍的高清图已经存在. .icon{ backgrou ...

  7. 查找java文件

    想要在eclipse里找一个类文件,可以用快捷键CTRL + SHIFT + R,但是有些文件是jar包里的类文件,可以用下图方法,创建一个变量,然后按住CTRL键,点击类名称 这样就找到jar文件了

  8. hint: not have locally. This is usually caused by another repository pushing

    git 提交代码前先pull代码,否则会报如下错误 wangju@wangju-HP-348-G4:~/test/reponselogiccheck$ git statusOn branch mast ...

  9. exist not exist 分析

    结果集1 结果集2: 最后连接条件 执行过程: 一行一行遍历结果集1的数据,然后结果集1中的连接条件执行子查询,如果有值返回那么在看是exist  还是not exist 在决定最后的结果集是否要要不 ...

  10. 从零搭建流媒体服务器+obs推流直播

    背景介绍 本文使用的流媒体服务器的搭建是基于rtmp(Real Time Message Protocol)协议的,rtmp协议是应用层的协议,要依靠底层的传输层协议,比如tcp协议来保证信息传输的可 ...