本章知识摘自:http://blog.csdn.net/creatorx/article/details/71100840

主要是在trie树的基础上,添加fail指针。

实际上,在AC自动机中的trie树中的每个节点,维护的是一个后缀树,而对于一个节点i的fail指针所指向的节点j需满足:j为i的最优后缀(即i的最长的且存在的后缀)

先copy上一章所说的trie树的节点构造及插入过程。

struct node{
    node* next[26];
    node* fail;
    int sum;
};
node* New(){
    node*nod=(node*)malloc(sizeof(struct node));
    rep(i,0,25)nod->next[i]=0;
    nod->sum=0;nod->fail=0;
    return nod;
}
node*root=New();
void Insert(char*s){
    node*p=root;
    int len=strlen(s);
    rep(i,0,len-1){
        int x=s[i]-'a';
        if(p->next[x]==NULL)p->next[x]=New();
        p=p->next[x];
    }
    p->sum++;
}

以下是构造fail指针的过程:

void getfail(){
    queue<node*>q;//BFS思想
    node*p=root;
    rep(i,0,25){
        if(p->next[i]){
            q.push(p->next[i]);
            p->next[i]->fail=root;
        }
    }
    while(!q.empty()){
        node*temp=q.front();q.pop();
        rep(i,0,25){
            if(temp->next[i]){
                q.push(temp->next[i]);
                p=temp->fail;//类似于KMP算法
                while(p){
                    if(p->next[i]){
                        temp->next[i]->fail=p->next[i];
                        break;
                    }
                    p=p->fail;
                }
                if(p==NULL)temp->next[i]->fail=root;
            }
        }
    }
}

访问文本串时也是用KMP算法的思想:

int Find(char*s){
    int len=strlen(s),tot=0;
    node*p=root;
    rep(i,0,len-1){
        int x=s[i]-'a';
        while(!p->next[x]&&p!=root)p=p->fail;//当前字符失配,则跳转至其最优后缀
        p=p->next[x];
        if(p==NULL)p=root;
        node*temp=p;
        while(temp!=root){//当前节点所代表的字符串在文本串中出现过,则其任意后缀也在文本串中出现过
            if(temp->sum>=0){
                tot+=temp->sum;
                temp->sum=-1;//当前节点已经计过数,不再重复计数
            }
            else break;
            temp=temp->fail;
        }
    }
    return tot;//返回有多少个模式串在文本串中出现过
}