病毒的侵扰和再侵扰两道AC自动机的应用
HDU2896 病毒的侵扰
http://vjudge.net/problem/viewProblem.action?id=16404
题目大意:
记录每个病毒的编号,并给出一些网站的源码,分别输出网站及其对应编号中所含病毒的编号,没有就不输出
最后输出有病毒网站的个数
这道题需要注意的是这个所有ASCII码均会用到,所以我之前傻傻地写str[i]-'a'还不知为什么会错简直苦逼~~
这里直接用ch[now][str[i]]找到对应位置即可
因为要记录编号,为了防止重复访问,我对query中进行了一个visit[]数组访问进行判断的操作
query函数如下:
void query(char *str){
int len=strlen(str);
int now=root,ret=;
for(int i=;i<len;i++){
now=ch[now][str[i]];
int k=now;
while(k!=root&&(!visit[k]&&val[k])){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
if(!visit[k])sum++,ans[ret++]=val[k],visit[k]=;
k=last[k];
}
}
}
用ans[]记录所有编号,sum记录病毒个数,那么就可以在main函数进行sort(ans,ans+sum)进行排序好就可以输出了
总代码如下:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define N 500*201
char str[];
int ans[],visit[N],sum;
struct AC{
int ch[N][],fail[N],val[N],last[N],tmp,root,cnt;
int newnode(){
val[tmp]=;
memset(ch[tmp],,sizeof(ch[tmp]));
return tmp++;
}
void init(){
tmp=,cnt=;
root=newnode();
}
void add(char *s){
int len=strlen(s);
int now=root;
for(int i=;i<len;i++){
int &k=ch[now][s[i]];
if(!k) k=newnode();
now=k;
}
cnt++;
val[now]=cnt;
}
void get_fail(){
fail[root]=root;
queue<int> q;
for(int i=;i<;i++){
int v=ch[root][i];
if(v)
fail[v]=last[v]=,q.push(v);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=;i<;i++){
int v=ch[now][i];
if(!v) ch[now][i]=ch[fail[now]][i];
else{
fail[v]=ch[fail[now]][i];
last[v]=val[fail[v]]?fail[v]:last[fail[v]];
q.push(v);
}
}
}
}
void query(char *str){
int len=strlen(str);
int now=root,ret=;
for(int i=;i<len;i++){
now=ch[now][str[i]];
int k=now;
while(k!=root&&(!visit[k]&&val[k])){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
if(!visit[k])sum++,ans[ret++]=val[k],visit[k]=;
k=last[k];
}
}
}
}ac;
int main()
{
int n,m;
while(scanf("%d",&n)!=EOF){
memset(ans,,sizeof(ans));
ac.init();
for(int i=;i<n;i++){
scanf("%s",str);
ac.add(str);
}
ac.get_fail();
scanf("%d",&m);
int c=;
for(int i=;i<=m;i++){
sum=;
scanf("%s",str);
memset(visit,,sizeof(visit));
ac.query(str);
sort(ans,ans+sum);
if(sum>){
printf("web %d:",i);
for(int j=;j<sum;j++) printf(" %d",ans[j]);
printf("\n");
c++;
}
}
printf("total: %d\n",c);
}
return ;
}
HDU 3065病毒再侵扰
http://vjudge.net/problem/viewProblem.action?id=16405
给一堆带序号的病毒,再给一个网站源码,问这个网站有哪些病毒,分别有几个,输出病毒码和其对应的个数
这里的query主串中因为会重复模式串,所以不能将val[]数组进行清零,要让他每次都能访问到
void query(char *str){
int len=strlen(str);
int now=root;
for(int i=;i<len;i++){
now=ch[now][str[i]];
int k=now;
while(k!=root){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
if(val[k]>) ans[val[k]]++;
k=last[k];
}
}
}
这道题和上一道字符串有所区别,这里病毒码只有26个大写字母,主串可以是128个ASCII码的任何字符
当然直接在AC结构体中定义ch[N][128]也并未超内存
这是我最开始写的代码:
| Memory: 16756 KB | Time: 296 MS | |
| Language: G++ |
Result: Accepted |
#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <string>
using namespace std;
#define N 1000*52
char str[];
int ans[];
struct AC{
int ch[N][],fail[N],val[N],last[N],tmp,root;
int newnode(){
val[tmp]=;
memset(ch[tmp],,sizeof(ch[tmp]));
return tmp++;
}
void init(){
tmp=;
root=newnode();
}
void add(string s,int cnt){
int len=s.length();
int now=root;
for(int i=;i<len;i++){
int &k=ch[now][s.at(i)];
if(!k) k=newnode();
now=k;
}
val[now]=cnt;
}
void get_fail(){
fail[root]=root;
queue<int> q;
for(int i=;i<;i++){
int v=ch[root][i];
if(v)
fail[v]=last[v]=,q.push(v);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=;i<;i++){
int v=ch[now][i];
if(!v) ch[now][i]=ch[fail[now]][i];
else{
fail[v]=ch[fail[now]][i];
last[v]=val[fail[v]]?fail[v]:last[fail[v]];
q.push(v);
}
}
}
}
void query(char *str){
int len=strlen(str);
int now=root;
for(int i=;i<len;i++){
now=ch[now][str[i]];
int k=now;
while(k!=root){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
if(val[k]>) ans[val[k]]++;
k=last[k];
}
}
}
}ac;
int main()
{
int n;
string s[];
while(scanf("%d",&n)!=EOF){
memset(ans,,sizeof(ans));
ac.init();
for(int i=;i<n;i++){
cin>>s[i+];
ac.add(s[i+],i+);
}
ac.get_fail();
scanf("%s",str);
ac.query(str);
for(int i=;i<=;i++){
if(ans[i]>) cout<<s[i]<<": "<<ans[i]<<endl;
}
}
return ;
}
但是我们定义一个ch[N][26]的数组却可以减少更多的内存占用,那么我们每次找位置都是用now=ch[now][str[i]-'A']来进行操作
在query中面对不在A~Z范围内的数,我们就利用一个if判断条件来做
if(str[i]<'A'||str[i]>'Z') now=root;//因为不在A~Z范围内的数是不存在有字母能跟它进行匹配的,所以直接将指针移回根节点重新进行判断
else{
}
| Memory: 5584 KB | Time: 187 MS | |
| Language: G++ | Result: Accepted |
#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <string>
using namespace std;
#define N 1000*52
char str[];
int ans[];
struct AC{
int ch[N][],fail[N],val[N],last[N],tmp,root;
int newnode(){
val[tmp]=;
memset(ch[tmp],,sizeof(ch[tmp]));
return tmp++;
}
void init(){
tmp=;
root=newnode();
}
void add(string s,int cnt){
int len=s.length();
int now=root;
for(int i=;i<len;i++){
int &k=ch[now][s.at(i)-'A'];
if(!k) k=newnode();
now=k;
}
val[now]=cnt;
}
void get_fail(){
fail[root]=root;
queue<int> q;
for(int i=;i<;i++){
int v=ch[root][i];
if(v)
fail[v]=last[v]=,q.push(v);
}
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=;i<;i++){
int v=ch[now][i];
if(!v) ch[now][i]=ch[fail[now]][i];
else{
fail[v]=ch[fail[now]][i];
last[v]=val[fail[v]]?fail[v]:last[fail[v]];
q.push(v);
}
}
}
}
void query(char *str){
int len=strlen(str);
int now=root;
for(int i=;i<len;i++){
if(str[i]>'Z'||str[i]<'A') now=root;
else{
now=ch[now][str[i]-'A'];
int k=now;
while(k!=root){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
if(val[k]>) ans[val[k]]++;
k=last[k];
}
}
}
}
}ac;
int main()
{
int n;
string s[];
while(scanf("%d",&n)!=EOF){
memset(ans,,sizeof(ans));
ac.init();
for(int i=;i<n;i++){
cin>>s[i+];
ac.add(s[i+],i+);
}
ac.get_fail();
scanf("%s",str);
ac.query(str);
for(int i=;i<=;i++){
if(ans[i]>) cout<<s[i]<<": "<<ans[i]<<endl;
}
}
return ;
}
病毒的侵扰和再侵扰两道AC自动机的应用的更多相关文章
- 一道cf水题再加两道紫薯题的感悟
. 遇到一个很大的数除以另一个数时,可以尝试把这个很大的数进行,素数因子分解. . 遇到多个数的乘积与另一个数的除法时,求是否能整除,可以先求每一个数与分母的最大公约数,最后若分母数字为1,则证明可整 ...
- [hdu3065]病毒侵袭持续中(AC自动机)
题意:给出多种病毒的号码和特征码,计算在某串中各病毒匹配的次数. 解题关键:AC自动机模板题,多组输入坑人. #include<bits/stdc++.h> using namespace ...
- ACM/ICPC 之 两道dijkstra练习题(ZOJ1053(POJ1122)-ZOJ1053)
两道较为典型的单源最短路径问题,采用dijkstra解法 本来是四道练习题,后来发现后面两道用dijkstra来解的话总觉得有点冗余了,因此暂且分成三篇博客(本篇以及后两篇). ZOJ1053(POJ ...
- 两道面试题,带你解析Java类加载机制
文章首发于[博客园-陈树义],点击跳转到原文<两道面试题,带你解析Java类加载机制> 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Gr ...
- 【转】两道面试题,带你解析Java类加载机制(类初始化方法 和 对象初始化方法)
本文转自 https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html 关键语句 我们只知道有一个构造方法,但实际上Ja ...
- 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester
这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...
- (转)关于inode和block的两道企业面试题
关于inode和block的两道企业面试题 原文:http://www.tk4479.net/xiaolong361/article/details/52373374 一.一个100M的磁盘分区,分别 ...
- 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制
你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...
- 穷举(四):POJ上的两道穷举例题POJ 1411和POJ 1753
下面给出两道POJ上的问题,看如何用穷举法解决. [例9]Calling Extraterrestrial Intelligence Again(POJ 1411) Description A mes ...
随机推荐
- K-th Number 线段树的区间第K大
http://poj.org/problem?id=2104 由于这题的时间限制不紧,所以用线段树水一水. 每个节点保存的是一个数组. 就是对应区间排好序的数组. 建树的时间复杂度需要nlogn 然后 ...
- R语言中的并行处理
网上有人说foreach包可以并行,我就去弄了,结果发现一个普通的二重循环什么事都不错都很卡!捣鼓了半天才发现是foreach的问题 为了提速,做了如下事宜: 直接利用矩阵列加减,不是一个个遍历加 把 ...
- vue_resource 使用说明
前几天用vue-resource调用接口,用post方式给后端,发现后端php接受不到数据,这好奇怪,最后发现提交给后端的时候 需要加一个参数 就是:emulateJSON : true 这句话的意思 ...
- img 标签访问图片返回403forbidden
做百度编辑器时,从秀米复制过来的文档,图片不无法加载,返回403的错 解决办法 解决这个问题只需要在头部添加一个meta <meta name="referrer" cont ...
- AJPFX简述Context.startService()和Context.bindService
Context.startService()和Context.bindService 服务不能自己运行,需要通过调用Context.startService()或Context.bindService ...
- canvas基础绘制-绚丽时钟
效果图: 与canvas基础绘制-绚丽倒计时的代码差异: // var endTime = new Date();//const声明变量,不可修改,必须声明时赋值: // endTime.setTim ...
- Java集合类工具CollectionUtils的操作方法
集合判断: 例1: 判断集合是否为空:CollectionUtils.isEmpty(null): trueCollectionUtils.isEmpty(new ArrayList()): true ...
- XML基本概念及增删改查操作
一.概念及特征: 1. XML 指可扩展标记语言(Extensible Markup Language),用户可以自己定义标签.XML 被设计用来传输和存储数据,而 HTML 用于格式化并显示数据,并 ...
- C# string日期格式
百分数格式应该用“p”这个参数. 格式 原始 数据 结 果 "{0:P}" 0.40 40% 数字 {0:N2} 12.36 数字 {0:N0} 13 货币 {0:c2} $1 ...
- Sublime Text插件的离线安装-使用htmlprettify美化您的HTML代码
Sublime Text是广大程序员喜欢的文本编辑器,Jerry觉得它最强大之处在于开放的架构,有丰富的插件为其提供各种各样额外的功能. 作为前端开发人员,经常需要从网上复制粘贴一些代码然后做修改,因 ...