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;
}