病毒的侵扰和再侵扰两道AC自动机的应用

HDU2896 病毒的侵扰

http://vjudge.net/problem/viewProblem.action?id=16404

题目大意:

记录每个病毒的编号,并给出一些网站的源码,分别输出网站及其对应编号中所含病毒的编号,没有就不输出

最后输出有病毒网站的个数

这道题需要注意的是这个所有ASCII码均会用到,所以我之前傻傻地写str[i]-‘a‘还不知为什么会错简直苦逼~~

这里直接用ch[now][str[i]]找到对应位置即可

因为要记录编号,为了防止重复访问,我对query中进行了一个visit[]数组访问进行判断的操作

query函数如下:

 1  void query(char *str){
 2         int len=strlen(str);
 3         int now=root,ret=0;
 4         for(int i=0;i<len;i++){
 5             now=ch[now][str[i]];
 6             int k=now;
 7             while(k!=root&&(!visit[k]&&val[k])){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
 8                 if(!visit[k])sum++,ans[ret++]=val[k],visit[k]=1;
 9                 k=last[k];
10             }
11         }
12     }

用ans[]记录所有编号,sum记录病毒个数,那么就可以在main函数进行sort(ans,ans+sum)进行排序好就可以输出了

总代码如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <queue>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 500*201
 7 char str[10005];
 8 int ans[505],visit[N],sum;
 9 struct AC{
10     int ch[N][128],fail[N],val[N],last[N],tmp,root,cnt;
11     int newnode(){
12         val[tmp]=0;
13         memset(ch[tmp],0,sizeof(ch[tmp]));
14         return tmp++;
15     }
16     void init(){
17         tmp=0,cnt=0;
18         root=newnode();
19     }
20     void add(char *s){
21         int len=strlen(s);
22         int now=root;
23         for(int i=0;i<len;i++){
24             int &k=ch[now][s[i]];
25             if(!k) k=newnode();
26             now=k;
27         }
28         cnt++;
29         val[now]=cnt;
30     }
31     void get_fail(){
32         fail[root]=root;
33         queue<int> q;
34         for(int i=0;i<128;i++){
35             int v=ch[root][i];
36             if(v)
37                 fail[v]=last[v]=0,q.push(v);
38         }
39         while(!q.empty()){
40             int now=q.front();
41             q.pop();
42             for(int i=0;i<128;i++){
43                 int v=ch[now][i];
44                 if(!v) ch[now][i]=ch[fail[now]][i];
45                 else{
46                     fail[v]=ch[fail[now]][i];
47                     last[v]=val[fail[v]]?fail[v]:last[fail[v]];
48                     q.push(v);
49                 }
50             }
51         }
52     }
53     void query(char *str){
54         int len=strlen(str);
55         int now=root,ret=0;
56         for(int i=0;i<len;i++){
57             now=ch[now][str[i]];
58             int k=now;
59             while(k!=root&&(!visit[k]&&val[k])){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
60                 if(!visit[k])sum++,ans[ret++]=val[k],visit[k]=1;
61                 k=last[k];
62             }
63         }
64     }
65 }ac;
66 int main()
67 {
68     int n,m;
69     while(scanf("%d",&n)!=EOF){
70         memset(ans,0,sizeof(ans));
71         ac.init();
72         for(int i=0;i<n;i++){
73             scanf("%s",str);
74             ac.add(str);
75         }
76         ac.get_fail();
77         scanf("%d",&m);
78         int c=0;
79         for(int i=1;i<=m;i++){
80             sum=0;
81             scanf("%s",str);
82             memset(visit,0,sizeof(visit));
83             ac.query(str);
84             sort(ans,ans+sum);
85             if(sum>0){
86                 printf("web %d:",i);
87                 for(int j=0;j<sum;j++) printf(" %d",ans[j]);
88                 printf("\n");
89                 c++;
90             }
91         }
92         printf("total: %d\n",c);
93     }
94     return 0;
95 }

HDU 3065病毒再侵扰

http://vjudge.net/problem/viewProblem.action?id=16405

给一堆带序号的病毒,再给一个网站源码,问这个网站有哪些病毒,分别有几个,输出病毒码和其对应的个数

这里的query主串中因为会重复模式串,所以不能将val[]数组进行清零,要让他每次都能访问到

 1 void query(char *str){
 2         int len=strlen(str);
 3         int now=root;
 4         for(int i=0;i<len;i++){
 5             now=ch[now][str[i]];
 6             int k=now;
 7             while(k!=root){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
 8                 if(val[k]>0) ans[val[k]]++;
 9                 k=last[k];
10             }
11         }
12     }

这道题和上一道字符串有所区别,这里病毒码只有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[2000010];
int ans[1001];
struct AC{
    int ch[N][128],fail[N],val[N],last[N],tmp,root;
    int newnode(){
        val[tmp]=0;
        memset(ch[tmp],0,sizeof(ch[tmp]));
        return tmp++;
    }
    void init(){
        tmp=0;
        root=newnode();
    }
    void add(string s,int cnt){
        int len=s.length();
        int now=root;
        for(int i=0;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=0;i<128;i++){
            int v=ch[root][i];
            if(v)
                fail[v]=last[v]=0,q.push(v);
        }
        while(!q.empty()){
            int now=q.front();
            q.pop();
            for(int i=0;i<128;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=0;i<len;i++){
            now=ch[now][str[i]];
            int k=now;
            while(k!=root){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值,
                if(val[k]>0) ans[val[k]]++;
                k=last[k];
            }
        }
    }
}ac;
int main()
{
    int n;
    string s[1001];
    while(scanf("%d",&n)!=EOF){
        memset(ans,0,sizeof(ans));
        ac.init();
        for(int i=0;i<n;i++){
            cin>>s[i+1];
            ac.add(s[i+1],i+1);
        }
        ac.get_fail();
        scanf("%s",str);
        ac.query(str);
        for(int i=1;i<=1000;i++){
            if(ans[i]>0) cout<<s[i]<<": "<<ans[i]<<endl;
        }
    }
    return 0;
}

但是我们定义一个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[2000010];
int ans[1001];
struct AC{
    int ch[N][26],fail[N],val[N],last[N],tmp,root;
    int newnode(){
        val[tmp]=0;
        memset(ch[tmp],0,sizeof(ch[tmp]));
        return tmp++;
    }
    void init(){
        tmp=0;
        root=newnode();
    }
    void add(string s,int cnt){
        int len=s.length();
        int now=root;
        for(int i=0;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=0;i<26;i++){
            int v=ch[root][i];
            if(v)
                fail[v]=last[v]=0,q.push(v);
        }
        while(!q.empty()){
            int now=q.front();
            q.pop();
            for(int i=0;i<26;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=0;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]>0) ans[val[k]]++;
                    k=last[k];
                }
            }
        }
    }
}ac;
int main()
{
    int n;
    string s[1001];
    while(scanf("%d",&n)!=EOF){
        memset(ans,0,sizeof(ans));
        ac.init();
        for(int i=0;i<n;i++){
            cin>>s[i+1];
            ac.add(s[i+1],i+1);
        }
        ac.get_fail();
        scanf("%s",str);
        ac.query(str);
        for(int i=1;i<=1000;i++){
            if(ans[i]>0) cout<<s[i]<<": "<<ans[i]<<endl;
        }
    }
    return 0;
}

病毒的侵扰和再侵扰两道AC自动机的应用

时间: 2024-08-05 21:50:39

病毒的侵扰和再侵扰两道AC自动机的应用的相关文章

一道cf水题再加两道紫薯题的感悟

1. 遇到一个很大的数除以另一个数时,可以尝试把这个很大的数进行,素数因子分解. 2. 遇到多个数的乘积与另一个数的除法时,求是否能整除,可以先求每一个数与分母的最大公约数,最后若分母数字为1,则证明可整除.或者把分子上的每个数进行素数因子分解,分母上的数也进行素数因子分解,若分子上的与分母上相同素数因子进行比较,分子上的素数因子指数大于分母位置上的,则证明可整除. 3. 遇到乘法的时候注意越界问题. 4. 遇到求某一个区间内,满足某一特征的数的个数,而这个特征与因子,约数有关,尝试用埃筛的方法

两道拓扑排序的问题

多久没写东西了啊.... 两道拓扑排序Liv.1的题....方法是一样的~~ <拓扑排序·二> 题目:http://hihocoder.com/contest/hiho81/problem/1 一个电脑网路,单向边,如果存在边u->v,那么u的病毒会感染到v. 要点,不存在环!那么如果u的入度=0的话,那么u中的病毒数不会再变化. 想到拓扑排序.不断删去入度为0的点.每次删去节点u,如果存在u->v,那么病毒数 num[v] += num[u].问题解决. (用queue实现拓扑排

两道笔试题的感触

今天做了两道笔试题,收益良多.有些题,你会发现,虽然并不难,但是却很容易没有思路或者出错,这都是源自平时的不求甚解.很多知识点,自以为已经掌握,其实只是管中窥豹,可见一斑.不要一味墨守成规,也要用于思考,很多东西既要知其然,也要知其所以然.我一直觉得了解和精通中间差着十万八千里,看来还有很长一段路要走.只有比别人更早.更勤奋地努力,才能尝到更加成功的滋味.哈哈,跑题了. 下面看一下两道笔试题.一.大概简单地说一下,求下面这段代码的结果. new Thread(new Runnable() { p

ACM/ICPC 之 两道dijkstra练习题(ZOJ1053(POJ1122)-ZOJ1053)

两道较为典型的单源最短路径问题,采用dijkstra解法 本来是四道练习题,后来发现后面两道用dijkstra来解的话总觉得有点冗余了,因此暂且分成三篇博客(本篇以及后两篇). ZOJ1053(POJ1122)-FDNY to the Rescue! 1 //POJ1122-ZOJ1053 2 //dijkstra-需要记录路径 3 //给出n个路口的邻接矩阵,求给定多个火警到失火点的时间及任一路径 4 //注意输入最后一行时,cin.getline需要两次,猜测需要接受邻接矩阵最后一行其他字符

算法--两道百度笔试题

算法--两道百度笔试题 今天看到一位园友写了一篇关于百度的面试题的博客,成了评论头条,再下看了一下,非常感兴趣,那位博主的算法能力跟我一样需要提高,估计他的功力还在我之下,所以再下不才,在这里把自己的源码贴出来. 百度面试题(一):假设一整型数组存在若干正数和负数,现在通过某种算法使得该数组的所有负数在正数的左边,且保证负数和正数间元素相对位置不变.时空复杂度要求分别为:o(n)和o(1).          其实开始的时候我也是一头雾水,在纸上画画之后发现,其实就是一道变形的插入排序.幸运的是

从志愿军“断刀”再论敏捷之道(上篇)

从志愿军"断刀"再论敏捷之道(上篇) 作者:欧德张(原创) ??在现在的IT项目中,以往常用的是瀑布模型套路,这些年敏捷模式大受欢迎,关于敏捷,现在诸人开口PMI-ACP,闭口则SCRUM,又有诸多实践.案例遵行其要旨,但力行其法者,却是既有成功者,亦有失败者,还有勉强完结者,这是什么情况呢???先举个旁证,说说我家小孩学书法的事,她1年前在学校的安排下开始学习书法,刚开始学书法时,肯定是未得书法要义,只能按书法老师所教,倾心于首笔之顿,末笔之收,写横写竖都缓缓而行,务求直正,随笔起落

穷举(四):POJ上的两道穷举例题POJ 1411和POJ 1753

下面给出两道POJ上的问题,看如何用穷举法解决. [例9]Calling Extraterrestrial Intelligence Again(POJ 1411) Description A message from humans to extraterrestrial intelligence was sent through the Arecibo radio telescope in Puerto Rico on the afternoon of Saturday November 16

ACM/ICPC 之 SPFA范例两道(POJ3268-POJ3259)

两道以SPFA算法求解的最短路问题,比较水,第二题需要掌握如何判断负权值回路. POJ3268-Silver Cow Party //计算正逆最短路径之和的最大值 //Time:32Ms Memory:360K #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<algorithm> using namespace std; #define

两道有意思的题目

碰到两道有意思的题目,记录一下. 题目一: 问,对于任意一个正整数,是否存在一个它的倍数全是由1和0组成? 例如: 1 * 1 = 1 2 * 5 = 10  (2的5倍是10,10由1和0组成) 3 * 37 = 111 (3 的 37 倍是111,111 全部由1组成) 4 * 25 = 100 (4 的 25 倍是100,100 由1和0组成) 5 * 20 = 100 (5 的 20 倍是100,100由1 和 0 组成) …… 现在需要判断,随便给一个正整数,是否存在一个它的倍数满足题