【HDU 2896】病毒侵袭(AC自动机-用数组完成)

题目:点击打开题目链接

Problem  Description:

当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻。。。。在这样的时刻,人们却异常兴奋——我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事儿啊~~
但网路上总有那么些网站,开始借着民众的好奇心,打着介绍日食的旗号,大肆传播病毒。小t不幸成为受害者之一。小t如此生气,他决定要把世界上所有带病毒的网站都找出来。当然,谁都知道这是不可能的。小t却执意要完成这不能的任务,他说:“子子孙孙无穷匮也!”(愚公后继有人了)。
万事开头难,小t收集了好多病毒的特征码,又收集了一批诡异网站的源码,他想知道这些网站中哪些是有病毒的,又是带了怎样的病毒呢?顺便还想知道他到底收集了多少带病毒的网站。这时候他却不知道何从下手了。所以想请大家帮帮忙。小t又是个急性子哦,所以解决问题越快越好哦~~

Input:

第一行,一个整数N(1<=N<=500),表示病毒特征码的个数。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在20—200之间。
每个病毒都有一个编号,依此为1—N。
不同编号的病毒特征码不会相同。
在这之后一行,有一个整数M(1<=M<=1000),表示网站数。
接下来M行,每行表示一个网站源码,源码字符串长度在7000—10000之间。
每个网站都有一个编号,依此为1—M。
以上字符串中字符都是ASCII码可见字符(不包括回车)。

Output:

依次按如下格式输出按网站编号从小到大输出,带病毒的网站编号和包含病毒编号,每行一个含毒网站信息。
web 网站编号: 病毒编号 病毒编号 …
冒号后有一个空格,病毒编号按从小到大排列,两个病毒编号之间用一个空格隔开,如果一个网站包含病毒,病毒数不会超过3个。
最后一行输出统计信息,如下格式
total: 带病毒网站数
冒号后有一个空格。

Sample  Input:

3
aaa
bbb
ccc
2
aaabbbccc
bbaacc

Sample  Output:

web 1: 1 2 3
total: 1

思路:这道题是学姐从杭电拉出来的,用链表提交,wa了(内存超限),在外面提交的时候过了,但是发现他的内存是擦边儿卡过去的,在学姐拉的题里面就擦不了边儿了,必须把链表改成数组去实现AC自动机,才能AC。而且,我感觉还是数组方便,链表还得开辟空间,还得释放内存的,到最后还内存超限,还不如数组来得实在。其实代码都一样,只是把链表部分换成了数组。

My  DaiMa:

#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
#include<malloc.h>
#include<algorithm>
using namespace std;
const int allson = 130;///此题中字符是ASCII表中所有字符
char pattern[205];
char text[10005];
int node[100005][allson];///节点
int fail[100005];///失败指针
int num[100005];///用来存放某个节点的单词数
int id;
int a[5];///用来记录出现的病毒序号
int vis[505];///用来标记病毒是否已经出现过
/*初始化模式串,类似于构建字典树*/
void insertPattern(int i)
{
    int p = 0;
    int index = 0;
    while(pattern[index] != '\0')
    {
        int son = pattern[index];
        if(node[p][son] == -1)
        {
            memset(node[id],-1,sizeof(node[id]));
            num[id] = 0;
            node[p][son] = id++;///node用来存放编号
        }
        p = node[p][son];
        index++;
    }
    num[p] = i;///将单词的末尾字符的num定义为单词的序号,便于后期输出病毒序号

}
/*构建AC自动机,计算fail指针*/
void build_AC_automaton()
{
    int p = 0;
    queue<int> qu;
    qu.push(p);
    while(!qu.empty())
    {
        p = qu.front();
        qu.pop();
        for(int i = 0; i < allson; i++)
        {
            if(node[p][i] != -1)
            {
                if(p == 0)///如果是根节点的儿子,它们的fail指针指向根
                    fail[node[p][i]] = 0;
                else
                {
                    int temp = fail[p];
                    while(temp != -1)
                    {
                        if(node[temp][i] != -1)///如果在它的父亲的fail指针下的儿子中能找到跟它相同的字符
                        {                      ///就让它的fail指针指向此节点
                            fail[node[p][i]] = node[temp][i];
                            break;
                        }
                         temp = fail[temp];
                    }
                    if(temp == -1)///如果找不到,就让它的fail指针指向根
                        fail[node[p][i]] = 0;
                }
                qu.push(node[p][i]);
            }
        }
    }
}
/*遍历AC自动机,匹配文本串*/
int find_in_AC_automaton()
{
    int p = 0;
    int index = 0;
    int k = 0;
    while(text[index] != '\0')
    {
        int son = text[index];
        while(node[p][son] == -1 && p != 0)///失败的时候返回它的fail地方再尝试进行匹配
            p = fail[p];
        p = node[p][son];
        if(p == -1) p = 0;
        int temp = p;
        while(temp != 0 && num[temp] != 0 && vis[num[temp]] == 0)
        {
            a[k++] = num[temp];///记录出现的病毒的序号
            vis[num[temp]] = 1;///将出现过的病毒标记掉
            temp = fail[temp];
        }
        index++;
        if(k >= 3)
            break;
    }
    return k;
}
int main()
{
    int n,m;
    fail[0] = -1;
    memset(node[0],-1,sizeof(node[0]));
    num[0] = 0;
    id = 1;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%s",pattern);
        insertPattern(i);
    }
    build_AC_automaton();
    scanf("%d",&m);
    int total = 0;
    for(int i = 1; i <= m; i++)
    {
        scanf("%s",text);
        memset(vis,0,sizeof(vis));
        int flag = find_in_AC_automaton();
        if(flag != 0)
        {
            total++;
            printf("web %d:",i);
            sort(a,a+flag);///将病毒序号进行从小到大排序
            for(int j = 0; j < flag; j++)
                printf(" %d",a[j]);
            printf("\n");
        }
    }
    printf("total: %d\n",total);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值