首页 > 代码库 > 病毒的侵扰和再侵扰两道AC自动机的应用

病毒的侵扰和再侵扰两道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 }
View Code


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*52char 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;}
View Code

但是我们定义一个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*52char 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;}
View Code