BZOJ3172 TJOI2013 单词 AC自动机

题意:给定N个字符串,设S为N个字符串首尾相连组成的字符串,查询每个字符串在S中出现的次数。

题解:

首先我们在构造Trie的时候,将构造过程中经过的节点的cnt全部++,此时cnt中记录该模式串被多少个模式串包含。

如果A为B的子串,B为C的子串,显然A也是C的子串,因此我们从下往上回溯,每回溯到一个节点都x->fail->cnt+=x->cnt

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXK=26;
const int MAXN=200+2;
const int MAXL=1000000+2;
struct NODE{
    int cnt;
    NODE *child[MAXK],*fail;
}*root,*mark[MAXL],*q[MAXL];
int N;
char S[MAXL];

NODE *NewNode(){
    NODE *x=new NODE();
    memset(x,0,sizeof(NODE));
    return x;
}

void Insert(char *S,NODE *&x,int k){
    if(!x) x=NewNode();
    x->cnt++;
    if(!*S){
        mark[k]=x;
        return;
    }
    Insert(S+1,x->child[(*S)-'a'],k);
}

void Get_Fail(NODE *x){
    int l=0,r=0;

    for(int i=0;i<MAXK;i++)
        if(x->child[i]) x->child[i]->fail=root,q[++r]=x->child[i];

    NODE *p,*t;
    while(l<r){
        t=q[++l];
        for(int i=0;i<MAXK;i++)
            if(t->child[i]){
                p=t->fail;
                while(p!=root && !p->child[i]) p=p->fail;
                if(p->child[i]) p=p->child[i];
                t->child[i]->fail=p;

                q[++r]=t->child[i];
            }
            else t->child[i]=t->fail->child[i];
    }

    for(int i=r;i;i--) q[i]->fail->cnt+=q[i]->cnt;
}

int main(){
    root=NewNode();

    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        scanf("%s",S);
        Insert(S,root,i);
    }
    Get_Fail(root);

    for(int i=1;i<=N;i++) printf("%d\n",mark[i]->cnt);

    return 0;
}
View Code

 

posted @ 2017-02-27 22:03  WDZRMPCBIT  阅读(138)  评论(0编辑  收藏  举报