题目链接:http://zerojudge.tw/ShowProblem?problemid=b179
题意:每一天一个基因串,例如abc,分裂成4个abca,abcb,abcc,abcd,并且串自身长度减小1,变成bc。当长度为0时被回收站回收。给出m个病毒串,当一个串含有病毒串时马上将被送到医院。n天后回收站和医院的串各为多少?
思路:f[i][j][k]表示第i天长度为j的串在自动机的k节点上的串的个数。自动机上每个节点记录一个len。两种转移:
(1)向下增长1,比较简单,对于k的子节点h,f[i+1][j+1][h]+=f[i][j][k];
(2)自身减去1:若j=1则直接放回回收站;若当前节点k的len值大于j-1,那么找到k的fail节点h,f[i+1][j-1][h]+=f[i][j][k];否则f[i+1][j-1][k]+=f[i][j][k]。
struct node { int next[4],fail,len,flag; void init() { clr(next,0); fail=0; flag=len=0; } }; node a[N]; int e,n,m; void insert(char s[]) { int i,k,p=0; for(i=0;s[i];i++) { k=s[i]-'a'; if(a[p].next[k]==0) { a[e].init(); a[e].len=a[p].len+1; a[p].next[k]=e++; } p=a[p].next[k]; } a[p].flag=1; } queue<int> Q; void build() { int i,j,k,p,q; FOR0(i,4) if(a[0].next[i]) Q.push(a[0].next[i]); while(!Q.empty()) { k=Q.front(); Q.pop(); for(i=0;i<4;i++) { if(a[k].next[i]) { p=a[k].next[i]; q=a[k].fail; Q.push(p); a[p].fail=a[q].next[i]; a[p].flag|=a[a[p].fail].flag; } else { q=a[k].fail; a[k].next[i]=a[q].next[i]; } } } } char s[50005],str[10005]; int f[2][105][1505]; void up(int &x,int y) { x=(x+y)%10007; } void DP() { int p=0,i,j,k,t,h,temp,len=strlen(str); for(i=0;str[i];i++) { k=str[i]-'a'; while(p&&!a[p].next[k]) p=a[p].fail; p=a[p].next[k]; if(a[p].flag) break; } if(a[p].flag) { puts("0 1"); return; } int ans1=0,ans2=0,pre=0,cur=1; k=0; clr(f[0],0); f[0][len][p]=1; while(n--) { clr(f[cur],0); for(i=0;i<e;i++) if(!a[i].flag) for(j=1;j<=100;j++) if(f[pre][j][i]) { temp=f[pre][j][i]; if(j==1) up(ans1,temp); else if(a[i].len>j-1) { t=a[i].fail; up(f[cur][j-1][t],temp); } else up(f[cur][j-1][i],temp); for(h=0;h<4;h++) { t=a[i].next[h]; up(f[cur][j+1][t],temp); if(a[t].flag) up(ans2,temp); } } pre^=1; cur^=1; } printf("%d %d\n",ans1,ans2); } int main() { while(scanf("%s%d%d",str,&n,&m)!=-1) { a[0].init();e=1; int i; FOR0(i,m) RD(s),insert(s); build(); DP(); } return 0; }