Keywords Search

指针方式:

 /* Keywords Search */
# include <iostream>
# include <stdio.h>
# include <string.h>
# include <string>
# include <cstdlib>
# include <ctime>
# include <cmath>
# include <cctype>
# include <vector>
# include <deque>
# include <queue>
# include <stack>
# include <climits>
# include <bitset>
# include <set>
# include <map>
using namespace std; # define N
# define INF 0x3f3f3f3f
# define lowbit(x)(x&(-x)) struct node
{
int cnt;
node *next[];
node *fail;
node(){
cnt=;
fail=NULL;
memset(next, NULL, sizeof(next));
}
};
node *root;
char s[], str[N];
int n; void init()
{
root = new node;
} void _Insert(char *ss)
{
int len = strlen(ss);
node *p=root;
for(int i=; i<len; i++ )
{
int t=ss[i]-'a';
if( p->next[t]==NULL )
p->next[t]=new node;
p=p->next[t];
}
p->cnt++;
} void Build_the_fail()
{
root -> fail = NULL;
//node *p=root;
queue<node*>q;
q.push(root); while( !q.empty() )
{
node *cur=q.front();
q.pop(); for(int i=; i<; i++ )
{
if( cur->next[i]!=NULL )
{
if( cur==root )
cur->next[i]->fail = root;
else
{
node *p=cur->fail;
while( p!=NULL )
{
if( p->next[i]!=NULL )
{
cur->next[i]->fail = p->next[i];
break;
} else
{
p = p -> fail;
}
}
if( p==NULL )
cur->next[i]->fail = root;
}
q.push(cur->next[i]);
}
}
}
} int query(char *str)
{
node *cur=root;
int pos;
int cont=;
int len=strlen(str);
for(int i=; i<len; i++ )
{
pos=str[i]-'a';
while( cur->next[pos]==NULL && cur!=root )
{
cur = cur->fail;
}
cur = cur->next[pos];
if( cur==NULL )
cur = root;
node *temp=cur;
while( temp!=root )
{
if( temp->cnt!=- )
{
cont += temp-> cnt;
temp -> cnt = -;
}
temp = temp -> fail;
}
}
return cont;
} int main()
{
int t;
scanf("%d", &t);
while( t-- )
{
init();
scanf("%d", &n);
for(int i=; i<n; i++ )
{
scanf("%s", s);
_Insert(s);
}
Build_the_fail();
scanf("%s", str);
printf("%d\n", query(str));
}
return ;
}

数组方式:

 /* */
# include <iostream>
# include <algorithm>
# include <utility>
# include <memory>
# include <deque>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <list>
# include <vector>
# include <cassert>
# include <functional>
# include <bitset>
# include <cmath>
# include <cstdlib>
# include <climits>
# include <cstring>
# include <string>
using namespace std;
typedef long long ll; # define mem(a,b)(a,b,sizeof(a))
# define lowbit(x)(x&(-x))
# define lcm(a,b)(a*b/__gcd(a,b))
const ll mod=1e9+;
const int maxn=;
const double pi=acos(-1.0); struct ac_auto
{
int Next[maxn][], Fail[maxn], End[maxn];//Next[now][buf[i]-'a'] 是now节点存着buf[i]字符的子节点的编号
int L, root;//注意这是全局变量,L是编号,root是根节点 int newNode(){
for(int i=; i<; i++ )//26叉树
Next[L][i] = -;
End[L++] = ;
return L-;//返回节点编号
} void Initial(){
L=;
root = newNode();
} void Insert( char buf[] )
{
int len=strlen(buf);
int now = root;
for(int i=; i<len; i++ )
{
if( Next[now][buf[i]-'a'] == - )
Next[now][buf[i]-'a'] = newNode();//若子节点没有buf[i],则插入buf[i]
now = Next[now][buf[i]-'a'];//now是当前节点编号
}
End[now]++;//作为结束字符的编号+1,计算单词出现的次数
} void Build_the_fail()
{
queue<int>ans;
Fail[root] = root;//根节点的失败指针指向自己 for(int i=; i<; i++ )
{
if( Next[root][i]==- )
Next[root][i] = root;
else
{
Fail[Next[root][i]] = root;//根节点的子节点的失败指针指向根节点
ans.push(Next[root][i]);
}
} while( !ans.empty() )
{
int now = ans.front();
ans.pop(); for(int i=; i<; i++)
{
if( Next[now][i]==- )
Next[now][i] = Next[Fail[now]][i];//若buf[i]没有插入字典树,则将令其等于其父节点的失败指针指向的节点的子节点buf[i]字符所在的节点
else
{
Fail[Next[now][i]] = Next[Fail[now]][i];//若buf[i]已经插入,其失败指针就指向其父节点的失败指针指向的节点的子节点buf[i]所在的节点
ans.push(Next[now][i]);//
}
}
}
} int Query( char buf[] )
{
int now = root;//从根节点开始找,now初始化为root
int res = ;//结果
int len = strlen(buf); for(int i=; i<len; i++ )
{
now = Next[now][buf[i]-'a'];//当前节点的编号
int temp = now;
while( temp!=root )
{
res += End[temp];//只有找到单词最后一个字符所在位置End[temp]才会>0,否则为0,所以可以直接加
End[temp] = ;//加完后置为0
temp = Fail[temp];//拓展到失败指针指向的位置,继续找
}
}
return res;
}
}AC; const int MAXN = ;
int T, n;
char buf[MAXN]; int main()
{
ios::sync_with_stdio(false);
cin>>T;
while( T-- )
{
cin>>n;
AC.Initial();
for(int i=; i<n; i++ )
{
cin>>buf;
AC.Insert(buf);
}
AC.Build_the_fail();
cin>>buf;
cout<<AC.Query(buf)<<endl;
}
return ;
}

AC自动机入门经典题目(两种表达方式)的更多相关文章

  1. Assignment写作需要掌握的两种表达方式

    在正式开始写Assignment之前都会进行文献检索和整理,选择适合Assignment选题的文献资料进行阅读和引用.对于文献中与自己的观点高度相关的参考资料要如何具体引用,而不造成抄袭或者增加文章的 ...

  2. layui中弹出层的两种表达方式

    方式一: 定义js中定义html变量 方式二: 设置div :hidden:hidden 布局 数据表格自适应大小: 代码: <style> .btn-container { margin ...

  3. AC自动机入门

    Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. KMP算法很好的解决了单模式匹配问题,如果有了字典树的基础,我们可以完美的结合二者解决多 ...

  4. hdu2222 KeyWords Search AC自动机入门题

    /** 链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222 题意:题意:给定N(N <= 10000)个长度不大于50的模式串,再给定一个长度为L ...

  5. JavaScript 函数的两种声明方式

    1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...

  6. POJ 2299-Ultra-QuickSort-线段树的两种建树方式

    此题有两种建树方式! Description In this problem, you have to analyze a particular sorting algorithm. The algo ...

  7. Spring的核心api和两种实例化方式

    一.spring的核心api Spring有如下的核心api BeanFactory :这是一个工厂,用于生成任意bean.采取延迟加载,第一次getBean时才会初始化Bean Applicatio ...

  8. Web APi之认证(Authentication)两种实现方式【二】(十三)

    前言 上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话. 序言 对于所谓的认证说到底 ...

  9. Android中BroadcastReceiver的两种注册方式(静态和动态)详解

    今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...

随机推荐

  1. IDEA远程调试Ambari Server

    1.配置端口 Ambari Server默认配置了服务端的debug参数,端口为5005.如果要修改端口,可以在/usr/sbin/ambari_server_main.py文件中对应地方修改,直接改 ...

  2. C# 获取系统字体方法

    //需要引用命名空间 using System.Drawing; using System.Drawing.Text; //获取系统字体方法 public dynamic GetFontNames() ...

  3. vim打开多个文件、同时显示多个文件、在文件之间切换

    打开多个文件: 1.vim还没有启动的时候: 在终端里输入  vim file1 file2 ... filen便可以打开所有想要打开的文件 2.vim已经启动 输入 :open file 可以再打开 ...

  4. Python进阶(十四)----空间角度研究类,类与类之间的关系

    Python进阶(十四)----空间角度研究类,类与类之间的关系 一丶从空间角度研究类 对象操作对象属性 class A(): address = '沙河' def __init__(self, na ...

  5. 换个语言学一下 Golang (6)——控制流程

    Go语言的控制结构关键字只有if..else if..else ,for 和 switch. 而且在Go中,为了避免格式化战争,对程序结构做了统一的强制的规定.看下下面的例子. 请比较一下A程序和B程 ...

  6. kali之nmap

    nmap简介 Nmap,也就是Network Mapper,最早是Linux下的网络扫描和嗅探工具包.可以扫描主机.端口.并且识别端口所对应的协议,以及猜测操作系统 Ping扫描(-sP参数) TCP ...

  7. JUC - Monitor监控ThreadPoolExecutor

    JUC - Monitor监控ThreadPoolExecutor 一个自定义Monitor监控ThreadPoolExecutor的执行情况 TASK WokerTask class WorkerT ...

  8. vue 利用v-model实现父子组件数据双向绑定

    v-model父组件写法: v-model子组件写法: 子组件export default中的model:{}里面两个值,prop代表着我要和props的那个变量相对应,event表示着事件,我触发事 ...

  9. selenium 定位元素方法

    1.通过id定位元素 写法1: element = driver.find_element_by_id("kw") 写法2: from selenium.webdriver.com ...

  10. MySQL Percona Toolkit--pt-osc执行SQL命令

    pt-osc执行日志 在对数据量为100000的表tb004做DROP COLUMN操作,pt-osc工具日志为: Operation, tries, wait: analyze_table, , c ...