AC自动机---病毒侵袭持续中

HDU 3065

题目网址: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=110773#problem/C

Description

小t非常感谢大家帮忙解决了他的上一个问题。然而病毒侵袭持续中。在小t的不懈努力下,他发现了网路中的“万恶之源”。这是一个庞大的病毒网站,他有着好多好多的病毒,但是这个网站包含的病毒很奇怪,这些病毒的特征码很短,而且只包含“英文大写字符”。当然小t好想好想为民除害,但是小t从来不打没有准备的战争。知己知彼,百战不殆,小t首先要做的是知道这个病毒网站特征:包含多少不同的病毒,每种病毒出现了多少次。大家能再帮帮他吗?
 

Input

第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。 
接下来N行,每行表示一个病毒特征码,特征码字符串长度在1―50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。 
在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。 
 

Output

按以下格式每行一个,输出每个病毒出现次数。未出现的病毒不需要输出。 
病毒特征码: 出现次数 
冒号后有一个空格,按病毒特征码的输入顺序进行输出。 
 

Sample Input

3 AA BB CC ooxxCC%dAAAoen....END
 

Sample Output

AA: 2 CC: 1

Hint

 Hit: 题目描述中没有被提及的所有情况都应该进行考虑。比如两个病毒特征码可能有相互包含或者有重叠的特征码段。 计数策略也可一定程度上从Sample中推测。 

思路: 这题要注意多组输入,上一题不用多组输入就过了,这题居然必须多组输入…… 还有要注意每个测试样例结束后要要将结构体空间清理掉,否则会超内存。 解题过程,在AC自动机模板上在结构体中加一个标记,用来标记模式串(病毒的特征码)的序号。在计数过程中,不用把模式串标记为-1,因为要重复计数。

代码如下:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 50010
char str[2000010],keyword[1005][55];
int head,tail;
int C[1005];

struct node
{
    node *fail;
    node *next[95];
    int b;
    int count;
    node()
    {
        fail=NULL;
        b=0;
        count=-1;
        for(int i=0; i<95; i++)
            next[i]=NULL;
    }
}*q[N];
node *root;

void insert(char *str,int x) ///建立Trie
{
    int temp,len;
    node *p=root;
    len=strlen(str);
    for(int i=0; i<len; i++)
    {
        temp=str[i]-32;
        if(p->next[temp]==NULL)
            p->next[temp]=new node();
        p=p->next[temp];
    }
    p->count++;
    p->b=x;///在该串结束字符位置标记该串的序号;
}

void setfail() ///使用bfs初始化fail指针;
{
    q[tail++]=root;
    while(head!=tail)
    {
        node *p=q[head++];
        node *temp=NULL;
        for(int i=0; i<95; i++)
            if(p->next[i]!=NULL)
            {
                if(p==root) ///首字母的fail必指向根
                    p->next[i]->fail=root;
                else
                {
                    temp=p->fail; ///失败指针
                    while(temp!=NULL) ///2种情况结束:匹配为空or找到匹配
                    {
                        if(temp->next[i]!=NULL) ///找到匹配
                        {
                            p->next[i]->fail=temp->next[i];
                            break;
                        }
                        temp=temp->fail;
                    }
                    if(temp==NULL) ///为空则从头匹配
                        p->next[i]->fail=root;
                }
                q[tail++]=p->next[i]; ///入队(p->next[i]的fail指针已设置m完);
            }
    }
}

void query()
{
    int index,len;
    node *p=root;
    len=strlen(str);
    for(int i=0; i<len; i++)
    {
        index=str[i]-32;
        while(p->next[index]==NULL&&p!=root) p=p->fail;///跳转失败指针
        p=p->next[index];
        if(p==NULL)
            p=root;
        node *temp=p; ///p不动,temp计算后缀串
        while(temp!=root&&temp->count!=-1)
        {
            C[temp->b]++;
            temp=temp->fail;
        }
    }
    return ;
}

void free_(node *r)
{
    for(int i=0;i<95;i++)
    {
        if(r->next[i])
        free_(r->next[i]);
    }
    free(r);
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(C,0,sizeof(C));
        getchar();
        head=tail=0;
        root=new node();
        for(int i=1; i<=n; i++)
        {
            gets(keyword[i]);
            insert(keyword[i],i);
        }
        setfail();
        gets(str);
        query();
        for(int i=1; i<=n; i++)
        if(C[i]) printf("%s: %d\n",keyword[i],C[i]);
        free_(root);///释放开辟的结构体空间;
    }
    return 0;
}

 

posted @ 2016-03-29 21:18  茶飘香~  阅读(200)  评论(0编辑  收藏  举报