题目链接: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;
}