字符串匹配--AC自动机模板
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自动机模板的更多相关文章
- Match:Keywords Search(AC自动机模板)(HDU 2222)
多模匹配 题目大意:给定很多个字串A,B,C,D,E....,然后再给你目标串str字串,看目标串中出现多少个给定的字串. 经典AC自动机模板题,不多说. #include <iostream& ...
- HDU 3065 (AC自动机模板题)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3065 题目大意:多个模式串,范围是大写字母.匹配串的字符范围是(0~127).问匹配串中含有哪几种模 ...
- HDU 2896 (AC自动机模板题)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2896 题目大意:多个模式串.多个匹配串.其中串的字符范围是(0~127).问匹配串中含有哪几个模式串 ...
- 从0开始 数据结构 AC自动机 模板(from kkke)
AC自动机模板 2.4.1 头文件&宏&全局变量 #include <queue> #define MAXN 666666 #define MAXK 26//字符数量 st ...
- HDu-2896 病毒侵袭,AC自动机模板题!
病毒侵袭 模板题,不多说了.. 题意:n个不同的字符串分别代表病毒特征,给出m次查询,每次一个字符串(网址),求这个字符串中有几个病毒特征,分别从大到小输出编号,最后输出所有的带病毒网址个数.格式请看 ...
- HDU 2222 AC自动机模板题
题目: http://acm.hdu.edu.cn/showproblem.php?pid=2222 AC自动机模板题 我现在对AC自动机的理解还一般,就贴一下我参考学习的两篇博客的链接: http: ...
- HDU 2222(AC自动机模板题)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意:多个模式串.问匹配串中含有多少个模式串.注意模式串有重复,所以要累计重复结果. 解题 ...
- HDU 2222 (AC自动机模板题)
题意: 给一个文本串和多个模式串,求文本串中一共出现多少次模式串 分析: ac自动机模板,关键是失配函数 #include <map> #include <set> #incl ...
- hdu 2222 Keywords Search ac自动机模板
题目链接 先整理一发ac自动机模板.. #include <iostream> #include <vector> #include <cstdio> #inclu ...
随机推荐
- 雷林鹏分享:Ruby 循环
Ruby 循环 Ruby 中的循环用于执行相同的代码块若干次.本章节将详细介绍 Ruby 支持的所有循环语句. Ruby while 语句 语法 while conditional [do] code ...
- 16 Managing Undo
16 Managing Undo 从Oracle11g开始,在默认安装中oracle会自动管理undo, 典型安装中不需要DBA介入配置,然而,如果选择了flash back特性,你就需要进行一些un ...
- Assert.IsNotNull 方法(判断对象不为NULL)
Assert.IsNotNull 方法 Visual Studio 2012 其他版本 Visual Studio 2010 Visual Studio 2008 Visual Studio 20 ...
- DOM文档对象模型
- Delegate比较全面的例子(需整理)
将Delegate理解为接口,只有一个方法的接口,这样最容易理解.这个方法只有声明,没有实现,实现在别的类.(实际上应该把它看作函数指针,不过接口更容易理解些.) 在你的类中有一个Delegate就相 ...
- django - html
1.注释 单行注释: {# #} 多行注释: {%comment%} {%endcomment%} 2.访问元组 不用t[0],要用t.0. 例: {% for t in text %} {{t.0 ...
- CentOS 7中firewall防火墙详解和配置以及切换为iptables防火墙
官方文档介绍地址: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Gui ...
- POJ 3176 Cow Bowling(dp)
POJ 3176 Cow Bowling 题目简化即为从一个三角形数列的顶端沿对角线走到底端,所取得的和最大值 7 * 3 8 * 8 1 0 * 2 7 4 4 * 4 5 2 6 5 该走法即为最 ...
- C++模板详解——使用篇
假如我们需要取得两个变量中较大的变量,或许,我们可以通过重载的方式实现,如下. int max(int fir,int sec); float max(float fir,float sec); do ...
- Java——File类概述
body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...