HDU 3695:Computer Virus on Planet Pandora(AC自动机裸题,数组实现AC自动机)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3695

题目意思,给出n个模式串和一个文本串,求n个模式串在文本串正串,和反向串中出现的个数。

AC自动机裸题,把文本串在AC自动机上跑一遍,统计一下有多少模式串出现在正着的文本串,

然后将文本串逆序,再在AC自动机上跑一边,统计一下有多少模式串出现在反着的文本串。


不能用动态链表写,开始的时候用动态链表写,TLE了。静态链表没试过,不知道能不能过。

不过动态链表TLE后,我直接用数组写,活生生对着链表的代码改成数组形式的,差点没看晕过去。

不过完美AC了。


AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>

using namespace std;

const int allSon = 26;
int node[500000][allSon];    ///字典树节点
int fail[500000];            ///fail指针
int num[500000];             ///以某个节点结尾的单词数
char patten[1010];           ///用来存放模式串
char text[5100010];          ///用来存放文本串
char str[5100010];
int ans;                     ///放答案
int id;                      ///给节点编号
///将模式串插入到字典树
void insertPatten()
{
    int p = 0;
    int index = 0;
    while(patten[index]!='\0')
    {
        int uppercase = patten[index]-'A';
        if(node[p][uppercase]==-1)       ///对应的儿子不存在
        {
            memset(node[id],-1,sizeof(node[id]));
            num[id] = 0;
            fail[id] = -1;               ///fail指针置-1代表空
            node[p][uppercase] = id++;   ///相应的位置放置节点编号
        }
        p = node[p][uppercase];
        index++;
    }
    num[p]++;
}
///找失败指针,构造AC自动机
void build_AC_automaton()
{
    queue<int>qu;
    int p = 0;           ///最开始指向根节点
    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)          ///p是根节点
                {
                    fail[node[p][i]] = 0;  ///节点的fail指针指向根
                }
                else
                {
                    int temp = fail[p];
                    while(temp != -1)
                    {
                        if(node[temp][i] != -1)
                        {
                            fail[node[p][i]] = node[temp][i];
                            break;
                        }
                        temp = fail[temp];
                    }
                    if(temp == -1)
                    {
                        fail[node[p][i]] = 0;
                    }
                }
                qu.push(node[p][i]);
            }
        }
    }
}
///在AC自动机中进行多模式匹配
void find_in_AC_automaton()
{
    int p = 0;     ///根节点
    int index = 0;
    while(text[index]!='\0')
    {
        int uppercase = text[index]-'A';
        while(node[p][uppercase]==-1 && p!=0)
            p = fail[p];
        p = node[p][uppercase];
        if(p == -1) p = 0;
        int temp = p;
        while(temp!=0 && num[temp]!=-1)
        {
            ans += num[temp];
            num[temp] = -1;
            temp = fail[temp];
        }
        index++;
    }
}
///输入文本串
void inputText()
{
    scanf("%s",str);//puts(str);
    int res = 0;
    int pos = 0;
    bool flag = false;
    int len = strlen(str);
    for(int i = 0; i < len; i++)
    {
        if(str[i]>='A' && str[i]<='Z')
        {
            if(flag)
            {
                while(res--)
                {
                    text[pos++] = str[i];
                }
            }
            else text[pos++] = str[i];
        }
        if(str[i]=='[') flag = true;
        if(str[i]>='0' && str[i]<='9')  res = res*10+(str[i]-'0');
        if(str[i]==']')
        {
            res = 0;
            flag = false;
        }
    }
    text[pos] = '\0';///puts(text);
}
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(node[0],-1,sizeof(node[0]));      ///0号节点是空节点
        num[0] = 0;
        fail[0] = -1;
        id = 1;
        for(int i = 0; i < n; i++)
        {
            scanf("%s",patten);
            insertPatten();
        }
        inputText();
        build_AC_automaton();
        ans = 0;
        find_in_AC_automaton();
        int len = strlen(text);
        reverse(text,text+len);  ///反转一下求一次。
        find_in_AC_automaton();
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值