题意:给n个病毒字符串和一个程序字符串,若程序字符串包含某个病毒字符串或者它的反串,则包含这个病毒,问所给程序字符串包含多少个病毒?
分析:用病毒串和反串建立AC自动机,然后求包含多少病毒,但同一个病毒可能会被计算2次(如果病毒和它的反串都出现在程序中),对于每个病毒,它在自动机中都有2个结点代表自身结尾和反串结尾,我们对每个病毒都记录这2个结点,在统计的过程中可以把走过的结点打上标记,最后再统计哪些病毒的2个结点都被标记,说明被统计了2次,需要减去一次,这样就没问题了。但是题中说了程序串是压缩的,所以需要先需处理,而且还要考虑嵌套压缩的情况,当时就没考虑到……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> #include <queue> using namespace std; #define N 251 #define NODE 500010 #define LEN 5100010 int n,node,x[N],y[N]; int next[NODE][26],fail[NODE],cnt[NODE]; bool vis[NODE]; char S[LEN],tmp[LEN]; int newnode() { memset(next[node],0,sizeof(next[0])); fail[node]=cnt[node]=0; return node++; } void insert(char *s,int id) { int i,k,cur; for(i=cur=0; s[i]; i++) { k=s[i]-'A'; if(!next[cur][k]) next[cur][k]=newnode(); cur=next[cur][k]; } cnt[cur]++; x[id]=cur; for(i--,cur=0; i>=0; i--) { k=s[i]-'A'; if(!next[cur][k]) next[cur][k]=newnode(); cur=next[cur][k]; } cnt[cur]++; y[id]=cur; } void makenext() { queue<int>q; int u,v,k; q.push(0); while(!q.empty()) { u=q.front(),q.pop(); for(int k=0; k<26; k++) { v=next[u][k]; if(v) q.push(v); else next[u][k]=next[fail[u]][k]; if(u&&v) fail[v]=next[fail[u]][k]; } } } void read() { char c; int p=0,t=0; while(c=getchar(),c!='\n') { if(t==0&&(c>='A'&&c<='Z')) { S[p++]=c; } else if(t!=0&&(c>='A'&&c<='Z')) { while(t--)S[p++]=c; } else if(c=='['||c==']') { t=0; } else if(c>='0'&&c<='9') { t=t*10+c-48; } } S[p]='\0'; } int main() { int T; char s[1001]; scanf("%d",&T); while(T--) { node=0,newnode(); scanf("%d",&n); for(int i=0; i<n; i++) { scanf("%s\n",s); insert(s,i); } makenext(); read(); int ans=0,i,k,cur; memset(vis,0,sizeof(vis)); for(i=cur=0; S[i]; i++) { k=S[i]-'A'; cur=next[cur][k]; for(int t=cur; t&&cnt[t]!=-1; t=fail[t]) { ans+=cnt[t]; cnt[t]=-1; vis[t]=1; } } for(int i=0; i<n; i++) { if(vis[x[i]]&&vis[y[i]]) ans--; } printf("%d\n",ans); } return 0; }