ZOJ3430 Detect the Virus [AC自动机]
这道题好坑啊,整整调了我一下午!!
题意很简单,就是一个裸的自动机加上解码。编码是使用8位ASCII码连接字符,然后6位一取,结尾不足6位用0补上,每6位二进制数都会对应一个字符,形成了新的字符串。解码后最好用int存每一位的内容,写起来比较方便,因为可能会有'\0'这样的字符出现。。
一开始写了个位运算版本的解码,但是一直WA。于是写了个很暴力的模拟二进制解码,竟然A了,看来自动机部分没什么问题。然后又回过头来调我的位运算版解码,怎么算都没有错啊,但是就是一直WAWAWA。。。。最后绝望之中改了下数组大小,把字符串数组从2500改成了5000。。。然后,然后就神奇的A了。。跑了130ms。。这尼玛是什么状况,难道2048bytes不是说2048位么。。就算不是,为啥越界不给我RE。。就算不给RE,为啥暴力的解码就过了。。。
#include <stdio.h> #include <string.h> #include <queue> #define MAXN 40000 int n,m; char ss[5000]; int sn[2500],slen,ctk[256]; //解码 void codestr(char* s){ slen=0; for(int i=0,len=0,x=0;s[i]&&s[i]!='=';i++){ len+=6, x=x<<6|ctk[s[i]]; if(len>=8){ sn[slen++]=((x>>(len-8))&0xff); len-=8; } } } //自动机 int next[MAXN][256],fail[MAXN],cnt[MAXN],cal[MAXN],pos; int newnode(){ for(int i=0;i<256;i++)next[pos][i]=0; fail[pos]=cnt[pos]=cal[pos]=0; return pos++; } void insert(int *s,int len,int id){ int p=0; for(int i=0;i<len;i++){ int &x=next[p][s[i]]; p=x?x:x=newnode(); } cnt[p]=id; } void makenext(){ std::queue<int> q; q.push(0); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<256;i++){ int v=next[u][i]; if(v==0)next[u][i]=next[fail[u]][i]; else q.push(v); if(u!=0)fail[v]=next[fail[u]][i]; } } } int findvirus(int *s,int len,int id){ int ans=0; for(int i=0,p=0;i<len;i++){ p=next[p][s[i]]; for(int f=p;f&&cal[f]!=id;f=fail[f]){ cal[f]=id; if(cnt[f]>0)ans++; } } return ans; } int main(){ for(int c=0;c<256;c++){ if(c>='A'&&c<='Z')ctk[c]=c-'A'; else if(c>='a'&&c<='z')ctk[c]=c-'a'+26; else if(c>='0'&&c<='9')ctk[c]=c-'0'+52; else if(c=='+')ctk[c]=62; else if(c=='/')ctk[c]=63; } //freopen("test.in","r",stdin); while(scanf("%d",&n)!=EOF){ pos=0;newnode(); for(int i=1;i<=n;i++){ scanf("%s",ss); codestr(ss); insert(sn,slen,i); } makenext(); scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%s",ss); codestr(ss); int x=findvirus(sn,slen,i); printf("%d\n",x); } printf("\n"); } return 0; }