AC自动机果断是神一样的东西,我赶在比赛前学习还是有用的,AC自动机最基本的是可以解决多个模式串在一个长字符串中出现的种类数或次数:

我暂时还是修改大神们的模板的昂

满满个人注释版帮助自己理解版:

 //该程序不能判别相同模式串,因此若模式串重复,答案会将相同模式串当做不同的处理,因此若需要可以用map去重或修改insert
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int maxm=; //maxm是总结点数:约为字母数+++ char s[],word[];
int nxt[maxm][],tail[maxm],f[maxm],size; //nxt是结点指向不同字母的结点下标,tail是表示该结点为几个单词的词尾(可能需要计算重复的模式串情况),f是当不匹配时转跳到的结点下标,size是结点数 int newnode(){ //初始化整个trie或建立新的结点时,首先初始化当前结点所指向的26个字母的结点为0,表示暂时还没有指向的字母,然后暂定该结点不是单词尾结点,暂无失配时转跳位置(即转跳到根节点),返回结点标号
memset(nxt[size],,sizeof(nxt[size]));
f[size]=tail[size]=;
return size++;
} void insert(char s[]){ //构造trie,p为当前结点的上一个结点标号,初始为0;x即为当前结点(上个结点标号指向当前字母的结点)标号,若此结点还未出现过,那么就建立这个结点;然后更新p为当前结点标号以便后续操作
int i,p=;
for(i=;s[i];i++){
int &x=nxt[p][s[i]-'a'];
p=x?x:x=newnode();
}
tail[p]++; //此时仅将s串记录,即将s串结尾的结点加1,若无相同模式串,则此操作只会使所有串尾结点的tail值由0变为1,但有相同模式串,则会重复记录,需要去重可以用map或用tail[p]=1;语句来完成
} void makenxt(){ //利用bfs来构造失配指针
int i;
queue<int>q;
f[]=; //先将0结点挂的字母加入队列,失配指针指向0结点
for(i=;i<;i++){
int v=nxt[][i];
if(v){
f[v]=;
q.push(v);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(i=;i<;i++){
int v=nxt[u][i];
if(!v)nxt[u][i]=nxt[f[u]][i]; //当u结点没有i对应字母,则视为失配,将其指向失配后转跳到的结点所指向的i对应字母
else{
q.push(v); //u结点存在指向i的结点,则将所指向的结点下标加入队列
f[v]=nxt[f[u]][i]; //失配指针指向上个结点失配指针指向结点所挂当前字母的结点
}
}
}
} int query(char s[]){ //查询s串中模式串出现了多少种/次
int ans=,v=;
for(int i=;s[i];i++){
while(v&&!nxt[v][s[i]-'a'])v=f[v]; //先匹配直到没有失配
v=nxt[v][s[i]-'a'];
int tmp=v;
while(tmp){
ans+=tail[tmp];
tail[tmp]=; //这里加这句是为了仅计算出现多少种模式链,而若不加这句则可以计算累计出现多少次
tmp=f[tmp];
}
}
return ans;
} int main(){
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
size=,newnode();
for(int i=;i<n;i++){
scanf("%s",word);
insert(word);
}
makenxt();
scanf("%s",s);
printf("%d\n",query(s));
}
return ;
}

另:加上last数组的版本,last数组可以很大程度上缩短时间,但是也同样要考虑卡空间的问题,另外鹏神也说last比较适用于纯匹配问题,可能套算法的话用处不是非常大:

 // 有nxt数组版本。。该程序不能判别相同模式串,因此若模式串重复,答案会将相同模式串当做不同的处理,因此若需要可以用map去重或修改insert
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int maxm=; //maxm是总结点数:约为字母数+++ char s[],word[];
int nxt[maxm][],tail[maxm],f[maxm],size; //nxt是结点指向不同字母的结点下标,tail是表示该结点为几个单词的词尾(可能需要计算重复的模式串情况),f是当不匹配时转跳到的结点下标,size是结点数
int last[maxm]; //last指针是指向上一个是单词结尾的结点,由于是由失配指针拓展得到的,因此所指向的单词都是该结点表示的单词的后缀单词,但由于可能卡空间,所以虽然可以在时间上优化,但是有时并不使用 int newnode(){ //初始化整个trie或建立新的结点时,首先初始化当前结点所指向的26个字母的结点为0,表示暂时还没有指向的字母,然后暂定该结点不是单词尾结点,暂无失配时转跳位置(即转跳到根节点),返回结点标号
memset(nxt[size],,sizeof(nxt[size]));
f[size]=tail[size]=;
return size++;
} void insert(char s[]){ //构造trie,p为当前结点的上一个结点标号,初始为0;x即为当前结点(上个结点标号指向当前字母的结点)标号,若此结点还未出现过,那么就建立这个结点;然后更新p为当前结点标号以便后续操作
int i,p=;
for(i=;s[i];i++){
int &x=nxt[p][s[i]-'a'];
p=x?x:x=newnode();
}
tail[p]++; //此时仅将s串记录,即将s串结尾的结点加1,若无相同模式串,则此操作只会使所有串尾结点的tail值由0变为1,但有相同模式串,则会重复记录,需要去重可以用map或用tail[p]=1;语句来完成
} void makenxt(){ //利用bfs来构造失配指针
int i;
queue<int>q;
f[]=;
for(i=;i<;i++){ //首先将0结点(根节点)连接的字母结点加入队列,并定失配指针和last指针都指向0结点
int v=nxt[][i];
if(v){
f[v]=last[v]=;
q.push(v);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(i=;i<;i++){
int v=nxt[u][i];
if(!v)nxt[u][i]=nxt[f[u]][i]; //当u结点没有i对应字母,则视为失配,将其指向失配后转跳到的结点所指向的i对应字母
else{
q.push(v); //u结点存在指向i的结点,则将所指向的结点下标加入队列
f[v]=nxt[f[u]][i]; //设置这个结点的失配指针指向上个结点失配后的指向字母i的结点,由于bfs一定会从字典树浅层到深层,即从字符串短到长,而失配转跳后表示的字符串长度严格减少,所以只需要指向一次即可
last[v]=tail[f[v]]?f[v]:last[f[v]]; //若失配指针指向的结点是单词结尾,那么当前结点失配后就可以直接指向失配结点,即失配路径上的上一个单词结点,若失配结点不是单词结尾,就指向失配结点的last
}
}
}
} int query(char s[]){ //查询s串中模式串出现了多少种/次
int ans=,v=;
for(int i=;s[i];i++){
while(v&&!nxt[v][s[i]-'a'])v=f[v];
v=nxt[v][s[i]-'a'];
int tmp=v;
while(tmp){
ans+=tail[tmp];
tail[tmp]=; //这里加这句是为了仅计算出现多少种模式链,而若不加这句则可以计算累计出现多少次
tmp=last[tmp];
}
}
return ans;
} int main(){
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
size=,newnode();
for(int i=;i<n;i++){
scanf("%s",word);
insert(word);
}
makenxt();
scanf("%s",s);
printf("%d\n",query(s));
}
return ;
}

并没有注释复制可以用用用版:

 #include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int maxm=; char s[],word[];
int nxt[maxm][],tail[maxm],f[maxm],size;
int last[maxm]; int newnode(){
memset(nxt[size],,sizeof(nxt[size]));
f[size]=tail[size]=;
return size++;
} void insert(char s[]){
int i,p=;
for(i=;s[i];i++){
int &x=nxt[p][s[i]-'a'];
p=x?x:x=newnode();
}
tail[p]++;
} void makenxt(){
int i;
queue<int>q;
f[]=;
for(i=;i<;i++){
int v=nxt[][i];
if(v){
f[v]=last[v]=;
q.push(v);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(i=;i<;i++){
int v=nxt[u][i];
if(!v)nxt[u][i]=nxt[f[u]][i];
else{
q.push(v);
f[v]=nxt[f[u]][i];
last[v]=tail[f[v]]?f[v]:last[f[v]];
}
}
}
} int query(char s[]){
int ans=,v=;
for(int i=;s[i];i++){
while(v&&!nxt[v][s[i]-'a'])v=f[v];
v=nxt[v][s[i]-'a'];
int tmp=v;
while(tmp){
ans+=tail[tmp];
tail[tmp]=;
tmp=last[tmp];
}
}
return ans;
} int main(){
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
size=,newnode();
for(int i=;i<n;i++){
scanf("%s",word);
insert(word);
}
makenxt();
scanf("%s",s);
printf("%d\n",query(s));
}
return ;
}

字符串匹配--AC自动机模板的更多相关文章

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

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

  2. HDU 3065 (AC自动机模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3065 题目大意:多个模式串,范围是大写字母.匹配串的字符范围是(0~127).问匹配串中含有哪几种模 ...

  3. HDU 2896 (AC自动机模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2896 题目大意:多个模式串.多个匹配串.其中串的字符范围是(0~127).问匹配串中含有哪几个模式串 ...

  4. 从0开始 数据结构 AC自动机 模板(from kkke)

    AC自动机模板 2.4.1 头文件&宏&全局变量 #include <queue> #define MAXN 666666 #define MAXK 26//字符数量 st ...

  5. HDu-2896 病毒侵袭,AC自动机模板题!

    病毒侵袭 模板题,不多说了.. 题意:n个不同的字符串分别代表病毒特征,给出m次查询,每次一个字符串(网址),求这个字符串中有几个病毒特征,分别从大到小输出编号,最后输出所有的带病毒网址个数.格式请看 ...

  6. HDU 2222 AC自动机模板题

    题目: http://acm.hdu.edu.cn/showproblem.php?pid=2222 AC自动机模板题 我现在对AC自动机的理解还一般,就贴一下我参考学习的两篇博客的链接: http: ...

  7. HDU 2222(AC自动机模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意:多个模式串.问匹配串中含有多少个模式串.注意模式串有重复,所以要累计重复结果. 解题 ...

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

    题意: 给一个文本串和多个模式串,求文本串中一共出现多少次模式串 分析: ac自动机模板,关键是失配函数 #include <map> #include <set> #incl ...

  9. hdu 2222 Keywords Search ac自动机模板

    题目链接 先整理一发ac自动机模板.. #include <iostream> #include <vector> #include <cstdio> #inclu ...

随机推荐

  1. Unity中使用的一套敏感词过滤方式

    当项目中的敏感词数量不是很多的时候,直接用数组来遍历过滤其实也可以,但是具体的数量有多大,这个肯定不好说,因此,对.txt中的敏感词合理组织后再进行过滤就显得非常有必要了. 如上图,左边是txt中配置 ...

  2. Anagram 由颠倒字母顺序而构成的字

    2018-07-15 19:23:08 Valid Anagram 问题描述: 问题描述: 可以使用map来记录各个字符出现的个数,在O(n)的时间复杂度内完成,当然也可以使用排序算法在O(nlogn ...

  3. WPF使用Webbrowser操作网页的主要代码

    1,引用mshtml.dll using mshtml; 2,获取元素属性值 IHTMLDocument2 doc2=(IHTMLDocument)webbrowser1.Document; IHTM ...

  4. 20170813pptVBA批量插入图片

    Sub AddSldIn() Dim Pre As Presentation Dim NewSld As Slide Set Pre = Application.ActivePresentation ...

  5. Linux Used内存到底哪里去了?

    原创文章,转载请注明: 转载自系统技术非业余研究 本文链接地址: Linux Used内存到底哪里去了? 前几天 纯上 同学问了一个问题: 我ps aux看到的RSS内存只有不到30M,但是free看 ...

  6. php安装redis扩展全

    一.安装redis mac 下安装也可以使用 homebrew,homebrew 是 mac 的包管理器. 1.执行 brew install redis 2.启动 redis,可以使用后台服务启动  ...

  7. Deep Learning of Graph Matching 阅读笔记

    Deep Learning of Graph Matching 阅读笔记 CVPR2018的一篇文章,主要提出了一种利用深度神经网络实现端到端图匹配(Graph Matching)的方法. 该篇文章理 ...

  8. HDU-3631 Shortest Path (floyd)

    Description When YY was a boy and LMY was a girl, they trained for NOI (National Olympiad in Informa ...

  9. 无法安装Java,以下开关中存在错误:“0”

    无法安装Java,以下开关中存在错误:“0”:. 解决方法:以管理员运行

  10. Intel DAAL AI加速——支持从数据预处理到模型预测,数据源必须使用DAAL的底层封装库

    数据源加速见官方文档(必须使用DAAL自己的库): Data Management Numeric Tables Tensors Data Sources Data Dictionaries Data ...