HDU2222 Keywords Search AC自动机

每次第一道模板题都是非常有意义的,考试前夕费尽心思学了KMP,学了Trie树,就是为了学这个做铺垫的,这道题时著名的AC自动机模板题。个人理解AC自动机就是在一棵Trie树上求失配指针,然后实现了多模匹配。只需遍历一次文本串就能求出所有的内容。在下面的query代码里,因为不能重复计算相同的模板串,所以每次加上后temp->count=-1,表示已经算过,在while循环里temp->count!=-1的时候才进行失配其实是有意义的,当temp->count==-1时说明已经沿着失配指针匹配过一次了,所以就没有必要再往上跑一次。假如每次匹配完我令temp->count=0,然后while循环里少了temp->count!=-1的话也是可以过的,前者200ms,后者600ms,就是因为多了不必要的计算。下面贴一记代码

//

//  main.cpp

//  AC auto machine

//

//  Created by change on 14-1-24.

//  Copyright (c) 2014年 chanme. All rights reserved.

//

#include <iostream>

#include <cstring>

#include <queue>

#include <cstdio>

#include <algorithm>

#define MAXCHAR 26

#define maxn 500000

using namespace std;



struct TrieNode

{

    int count;

    TrieNode* next[MAXCHAR];

    TrieNode* fail;

    void init()

    {

        memset(next,0,sizeof(next));

        fail=NULL;

        count=0;

    }

}T[maxn],*Trie;

int top;



void insert(char *c)

{

    int len=(int)strlen(c);TrieNode *p=Trie;

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

        if(p->next[c[i]-'a']!=NULL){

            p=p->next[c[i]-'a'];

        }

        else{

            T[top].init();

            p->next[c[i]-'a']=&T[top++];

            p=p->next[c[i]-'a'];

        }

    }

    p->count++;

}



void getFail()

{

    queue<TrieNode *> que;  // 用于BFS的队列

    que.push(Trie); // 将根结点压入

    Trie->fail=NULL; // 根结点的失配指针为NULL

    while(!que.empty())

    {

        TrieNode* temp=que.front();

        que.pop();

        TrieNode* p=NULL;

        // 枚举每个后继

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

            // 后继非空时计算其失配指针

            if(temp->next[i]!=NULL) {

                // 根结点的所有后继的失配指针指向根结点

                if(temp==Trie){

                    temp->next[i]->fail=Trie;

                }

                else{

                    // 否则的话,沿着失配指针走,遇到有相同后继的则连失配指针

                    p=temp->fail;

                    while(p!=NULL){

                        if(p->next[i]!=NULL){

                            temp->next[i]->fail=p->next[i];

                            break;

                        }

                        p=p->fail;

                    }

                    // 否则也指向根结点

                    if(p==NULL) temp->next[i]->fail=Trie;

                }

                // 压入队列

                que.push(temp->next[i]);

            }

        }

    }

}



int query(char *c)

{

    int len=(int)strlen(c);

    int res=0;

    TrieNode *p=Trie;

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

        while(p->next[c[i]-'a']==NULL && p!=Trie) p=p->fail;

        p=p->next[c[i]-'a'];

        if(p==NULL) p=Trie;

        TrieNode *temp=p;

        while(temp!=Trie&&temp->count!=-1){

            res+=temp->count;

            temp->count=-1;

            temp=temp->fail;

        }

    }

    return res;

}



char str[55];

char text[1005000];

int n;





int main()

{

    int cas;cin>>cas;

    while(cas--)

    {

        top=0;T[top].init();

        Trie=&T[top++];

        cin>>n;

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

            scanf("%s",str);

            insert(str);

        }

        getFail();

        scanf("%s",text);

        printf("%d\n",query(text));

    }

    return 0;

}

 

你可能感兴趣的:(search)