ZOJ 3430 Detect the Virus(AC自动机+字符串转换)

ZOJ 3430 Detect the Virus(AC自动机+字符串转换)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3430

题意

        给你多个模板串,和一个文本串,现在问你文本串中出现了几个模板串。但是模板串和文本串都是用64位编码的。

分析

       由于数据都是64位编码的,所以需要先把模板串和文本串解码成ASCII字符串形式。

       解码函数:

1.    对于一个长n的64位编码字符串,末尾不带=号,那么它对应的2进制数为n*6位,对应的ASCII字符串有n*6/8 位。

2.    对于一个长n(这个n不包括末尾的=号,如果要算上1=号,那么就是n+1位的64位编码字符串)的64位编码字符串,末尾带一个=号,那么它对应的2进制数为n*6-2位,对应的ASCII字符串有(n*6-2)/8 位。

3.    对于一个长n的64位编码字符串,末尾带两个=号,那么它对应的2进制数为n*6-4位,对应的ASCII字符串有(n*6-4)/8 位。

首先将64位编码的字符串读入a[]中,先求串长n,然后在看末尾m个=号.则原本的2进制数为x=(n-m)*6-2*m 位.

       接下来将a[]数组解码成二进制数组b[],并且只取前x位.然后再将这x位二进制位编码成 x/8位ASCII字符串.存入s[]字符数组内.

注意:上面的解法方式是对的,但是会超时,现在用另一种方法来算.首先对于a中的每个字符解码到x中去(用位运算符& ,|),x=(x<<6)|mp[a[i]],len表示x中当前已经有多少位有效的2进制位了.那么只要len>=8,就立即把x的前8位转移到temp[]中去(temp[0]存放的是temp数组的长度).在构建AC自动机的时候,我们直接用temp构建即可不用char*数组构建了.

下面就是直接建立AC自动机匹配了,不过要注意的是模板可能重复,所以AC自动机中的val值表示单词节点出现的次数,如果该模板出现了1次就直接+1,否则值继续加即可.最后用文本串去匹配即可,遇到一个单词就用ans+上该单词的val值即可.

还要注意:如果一个模板在文本串中出现了2,那么只能算1.所以要用vis数组标记好.vis=true表示该单词节点还没有被加进ans,否则vis=false;

AC代码:

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
char a[5000];
int temp[5000];
int x;
char mp[256];
int ans;//匹配的模板个数

void convert(char *a)
{
    temp[0]=0;
    for(int i=0,len=0,x=0;a[i]&&a[i]!='=';i++)
    {
        len+=6;
        x=(x<<6)|mp[a[i]];
        if(len>=8)
        {
            temp[++temp[0]] = (x>>(len-8))&0xff;//移走x新生成的8位,不一定是最高8位,想想
            len-=8;
        }
    }
}
const int maxnode=50000;
const int sigma_size=256;
bool vis[maxnode];
struct AC_Automata
{
    int ch[maxnode][sigma_size];
    int val[maxnode];
    int f[maxnode];
    int last[maxnode];
    int sz;
    void init()
    {
        sz=1;
        f[0]=last[0]=val[0]=0;
        memset(ch[0],0,sizeof(ch[0]));
    }
    void insert(int *s)
    {
        int u=0;
        for(int i=1;i<=s[0];i++)
        {
            int id=s[i];
            if(ch[u][id]==0)
            {
                ch[u][id]=sz;
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz++]=0;
            }
            u=ch[u][id];
        }
        val[u]++;
    }
    void print(int i)
    {
        if(val[i])
        {
            if(!vis[i])
            {
                ans +=val[i];
                vis[i]=true;
            }
            print(last[i]);
        }
    }
    void find(int *s)
    {
        int j=0;
        for(int i=1;i<=s[0];i++)
        {
            int id=s[i];
            while(j && ch[j][id]==0) j=f[j];
            j=ch[j][id];
            if(val[j]) print(j);
            else if(last[j]) print(last[j]);
        }
    }
    void getFail()
    {
        f[0]=0;
        queue<int> q;
        for(int i=0;i<sigma_size;i++)
        {
            int u=ch[0][i];
            if(u)
            {
                last[u]=f[u]=0;
                q.push(u);
            }
        }
        while(!q.empty())
        {
            int r=q.front();q.pop();
            for(int i=0;i<sigma_size;i++)
            {
                int u=ch[r][i];
                if(!u) continue;
                q.push(u);
                int v=f[r];
                while(v && ch[v][i]==0) v=f[v];
                f[u]=ch[v][i];
                last[u]= val[f[u]]? f[u]:last[f[u]];
            }
        }
    }
};
AC_Automata ac;

void Init(){
    for(int i=0;i<26;++i)mp[i+'A']=i;
    for(int i=26;i<52;++i)mp[i-26+'a']=i;
    for(int i=52;i<62;++i)mp[i-52+'0']=i;
    mp['+']=62,mp['/']=63;
}

int main()
{
    Init();
    int n,m;
    while(scanf("%d",&n)!=EOF)
    {
        ac.init();
        for(int i=0;i<n;i++)
        {
            scanf("%s",a);
            convert(a);
            ac.insert(temp);
        }
        scanf("%d",&m);
        ac.getFail();
        while(m--)
        {
            memset(vis,0,sizeof(vis));
            ans=0;
            scanf("%s",a);
            convert(a);
            ac.find(temp);
            printf("%d\n",ans);
        }
        printf("\n");
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值