666 专题五 AC自动机

Problem A.Keywords Search

d.n个关键字,1段描述,求描述中出现了多少关键字

s.

c.

/*
ac自动机模板
n个关键字,1段描述,求描述中出现了多少关键字
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

struct Trie{
    int next[500010][26],fail[500010],end[500010];
    int root,L;
    int newnode(){
        for(int i=0;i<26;i++)
            next[L][i]=-1;
        end[L++]=0;
        return L-1;
    }
    void init(){
        L=0;
        root=newnode();
    }
    void insert(char buf[]){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++){
            if(next[now][buf[i]-'a']==-1)
                next[now][buf[i]-'a']=newnode();
            now=next[now][buf[i]-'a'];
        }
        end[now]++;
    }
    void build(){
        queue<int>Q;
        fail[root]=root;
        for(int i=0;i<26;i++)
            if(next[root][i]==-1)
            next[root][i]=root;
            else{
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        while(!Q.empty()){
            int now=Q.front();
            Q.pop();
            for(int i=0;i<26;i++)
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else{
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    int query(char buf[]){
        int len=strlen(buf);
        int now=root;
        int res=0;
        for(int i=0;i<len;i++){
            now=next[now][buf[i]-'a'];
            int temp=now;
            while(temp!=root){
                res+=end[temp];
                end[temp]=0;
                temp=fail[temp];
            }
        }
        return res;
    }
    void debug(){
        for(int i=0;i<L;i++){
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
            for(int j=0;j<26;j++)
                printf("%2d",next[i][j]);
            printf("]\n");
        }
    }
};

char buf[1000010];
Trie ac;

int main(){
    int T;
    int n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        ac.init();
        for(int i=0;i<n;i++){
            scanf("%s",buf);
            ac.insert(buf);
        }
        ac.build();
        scanf("%s",buf);
        printf("%d\n",ac.query(buf));
    }
    return 0;
}
View Code

 

Problem B.病毒侵袭

d.这题和上题差不多,要输出出现模式串的id,用end记录id就可以了。还有trie树的分支是128的

s.

c.

/*
ac自动机模板
n个关键字,1段描述,求描述中出现了多少关键字
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
using namespace std;

struct Trie{
    int next[510*210][128],fail[510*210],end[510*210];
    int root,L;
    int newnode(){
        for(int i=0;i<128;i++)
            next[L][i]=-1;
        end[L++]=-1;
        return L-1;
    }
    void init(){
        L=0;
        root=newnode();
    }
    void insert(char buf[],int id){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++){
            if(next[now][buf[i]]==-1)
                next[now][buf[i]]=newnode();
            now=next[now][buf[i]];
        }
        end[now]=id;
    }
    void build(){
        queue<int>Q;
        fail[root]=root;
        for(int i=0;i<128;i++)
            if(next[root][i]==-1)
            next[root][i]=root;
            else{
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        while(!Q.empty()){
            int now=Q.front();
            Q.pop();
            for(int i=0;i<128;i++)
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else{
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }

    set<int>myset;

    bool query(char buf[],int id){
        myset.clear();
        int len=strlen(buf);
        int now=root;

        for(int i=0;i<len;i++){
            now=next[now][buf[i]];
            int temp=now;
            while(temp!=root){
                if(end[temp]!=-1){
                    myset.insert(end[temp]);
                }
                temp=fail[temp];
            }
        }

        if(!myset.empty()){
            printf("web %d:",id);
            for(set<int>::iterator it=myset.begin();it!=myset.end();++it){
                printf(" %d",*it);
            }
            printf("\n");
            return true;
        }
        return false;
    }
    void debug(){
        for(int i=0;i<L;i++){
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
            for(int j=0;j<128;j++)
                printf("%2d",next[i][j]);
            printf("]\n");
        }
    }
};

char buf[10005];
Trie ac;

int main(){

    int N,M;
    int ans;

    while(~scanf("%d",&N)){
        ac.init();

        for(int i=1;i<=N;++i){
            scanf("%s",buf);
            ac.insert(buf,i);
        }

        ac.build();

        scanf("%d",&M);
        ans=0;
        for(int i=1;i<=M;++i){
            scanf("%s",buf);
            if(ac.query(buf,i)){
                ++ans;
            }
        }

        printf("total: %d\n",ans);

    }

    return 0;
}
View Code

 

Problem C.病毒侵袭持续中

d.这题的变化也不大,就是需要输出每个模式串出现的次数,查询的时候使用一个数组进行记录就可以了

s.

c.

/*
ac自动机模板
n个关键字,1段描述,求描述中出现了多少关键字
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

char str[1024][64];

struct Trie{
    int next[1005*55][26],fail[1005*55],end[1005*55];
    int root,L;
    int newnode(){
        for(int i=0;i<26;i++)
            next[L][i]=-1;
        end[L++]=-1;
        return L-1;
    }
    void init(){
        L=0;
        root=newnode();
    }
    void insert(char buf[],int id){
        int len=strlen(buf);
        int now=root;
        for(int i=0;i<len;i++){
            if(next[now][buf[i]-'A']==-1)
                next[now][buf[i]-'A']=newnode();
            now=next[now][buf[i]-'A'];
        }
        end[now]=id;
    }
    void build(){
        queue<int>Q;
        fail[root]=root;
        for(int i=0;i<26;i++)
            if(next[root][i]==-1)
                next[root][i]=root;
            else{
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        while(!Q.empty()){
            int now=Q.front();
            Q.pop();
            for(int i=0;i<26;i++)
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else{
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }

    int sum[1024];

    void query(char buf[],int N){
        memset(sum,0,sizeof(sum));
        int len=strlen(buf);
        int now=root;

        for(int i=0;i<len;i++){
            if(buf[i]<'A'||buf[i]>'Z'){
                now=root;//如果用128个来建字典树的话,没有的字符应该是指向root吧?应该是。
                continue;//(即下面的now=next[now][buf[i]]的值应该是root?)
                //使用这种判断的开辟26个字符即可,节省空间
            }
            now=next[now][buf[i]-'A'];
            int temp=now;
            while(temp!=root){
                if(end[temp]!=-1){
                    ++sum[end[temp]];
                }

                temp=fail[temp];
            }
        }

        for(int i=0;i<N;++i){
            if(sum[i]>0){
                printf("%s: %d\n",str[i],sum[i]);
            }
        }
    }
    void debug(){
        for(int i=0;i<L;i++){
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
            for(int j=0;j<26;j++)
                printf("%2d",next[i][j]);
            printf("]\n");
        }
    }
};

char buf[2000005];
Trie ac;

int main(){

    int N;

    while(~scanf("%d",&N)){
        ac.init();
        for(int i=0;i<N;i++){
            scanf("%s",str[i]);
            ac.insert(str[i],i);
        }
        ac.build();
        scanf("%s",buf);

        ac.query(buf,N);
    }
    return 0;
}
View Code

 

posted @ 2016-01-18 15:52  gongpixin  阅读(226)  评论(0)    收藏  举报