AC 自动机

学习AC自动机的前提是要会trie数和KMP字符串匹配, 它的功能是能对好多个模式串进行同时查找。

比如对4个模式串:

he

hers

his

she

在一条母串中:shejjjjj 查找每个模式串出现的次数.


我们知道KMP算法有个next数组,和KMP类似,AC自动机有一个fail指针数组,用来对整棵trie树进行滚动。

AC 自动机:

HUD 3065

#include<cstdio>

#include<cstring>

#include<queue>

using namespace std;

int ch[1002*52][26],End[1002*52],cur,fail[1002*52],last[1002*52],ans[1002];

char str[2000005],str0[1002][52];

void get_fail() {

    int now,tmpFail,Next;

    queue<int> q;

    //bfs生成fail

    //初始化队列

    for(int j=0;j<26;j++) {

        if(ch[0][j]) {

            q.push(ch[0][j]);

            fail[ch[0][j]] = 0;

            last[ch[0][j]] = 0;

        }

    }

    while(!q.empty()) {

        //从队列中拿出now

        //此时now中的faillast已经算好了

        //下面计算的是ch[now][j]中的faillast

        now = q.front();q.pop();

        for(int j=0;j<26;j++) { 

            if(!ch[now][j]) continue;

            Next = ch[now][j];

            q.push(Next);

            tmpFail = fail[now];//kjkhj

            while(tmpFail&&!ch[tmpFail][j]) tmpFail = fail[tmpFail];

            fail[Next] = ch[tmpFail][j];

            last[Next] = End[fail[Next]] ? fail[Next]:last[fail[Next]];

        }

    }

}

void Find(){

    int now = 0;

    int len = strlen(str);

    for(int i=0;i<len;i++){

        if(str[i]<'A'||str[i]>'Z') {now=0;continue;}

        str[i]-='A';

        while(now&&!ch[now][str[i]]) now = fail[now];

        now = ch[now][str[i]];

        if(End[now]) ans[End[now]]++;

        int tmp = now;

//重要理解

//这时候已经滚到了节点now,下面就需要找出所有以now为结尾的模式串,就需要用到last数组了。Last数组保存的是以节点now为结尾的模式串。

//比如 abcd  bcd  两个模式串,abcdd节点的last指向bcd中的d节点。

//当然两个d节点不是同一个。

//这样就能知道当滚到abcdd节点时,我们还同时找到了bcd这个串。

//如果存在,在找到abcd的同时,我们还找到了bcd  cd  d 这三个模式串。

//事实上,下面last数组滚过的结点,在之前可能从来没有被访问过。

//《训练指南》上的代码找的是包含模式串的一段母字符串,而不是找出所有出现过的模式串。

        while(last[tmp]) {

            ans[End[last[tmp]]]++;

            tmp = last[tmp];

        }

    }

}

int main(){

    int n,now;

    while(scanf("%d",&n)!=EOF){

    memset(ch,0,sizeof(ch));

    memset(End,0,sizeof(End));

    memset(ans,0,sizeof(ans));

    memset(last,0,sizeof(last));

    cur = 1;

    int len;

    for(int i=1;i<=n;i++) {

        scanf("%s",str0[i]);

        len = strlen(str0[i]);

        now = 0;

        for(int j=0;j<len;j++) {

            str0[i][j]-='A';

            if(ch[now][str0[i][j]]==0) ch[now][str0[i][j]] = cur++;

            now = ch[now][str0[i][j]];

            str0[i][j]+='A';

        }

        End[now] = i;

    }

    get_fail();

    scanf("%s",str);

    Find();

    for(int i=1;i<=n;i++) {

        if(ans[i])

            printf("%s: %d\n",str0[i],ans[i]);

    }

    }

}



你可能感兴趣的:(AC自动机)