http://zerojudge.tw/ShowProblem?problemid=b179
咩哈哈哈哈..这个只有29个人出的题俺整出来了...网上似乎还木有解题报告的说...我就来做第一个吧...
这种关于包含病毒就要不得的问题很明显的要用到AC自动机...不管三七二十一..先根据所有病毒串做好AC自动机以及Trie图...
本题的状态可以用dp[t][k][p]来表示.t代表当前"天"..k代表当前串长度...p代表当前串所能再AC自动机理匹配的最长后缀的末尾点...那么由于每一天只与前一天的状态有关..t可以用0,1的滚动数组来标记...k根据题目的说明最大100..p根据输入数据分析最大15*100=1500..so..dp[2][100][1500]可以记录题目所给的任何情况了...从时间上看,天数最多为300天..那么最多需要的操作数为300*100*1500=45000000..由于高中的OI比赛采用单点Judge...表示时间上也能接受...
观察本题转移..当前有一个串..除了加'a' 'b' 'c' 'd'利用Trie图进行转移这种很基本的操作..还有一个就是去掉自身的第一个字符..而本题的关键就是这个..而这个操作可以分为3种情况:
1.当前的长度为1..那么分裂完后显然就要丢资源回收站了..
2.当前长度大于当前p所在Trie树中的深度,那么其去掉首字母后还会停留在Trie树中的p点..
3.当前长度小于当前p所在Trie树中的深度,那么其去掉首字母后就需要转移了..沿着AC自动机的Fail指针去更新point[p].fail既是...
Program:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#define oo 2000000000
#define ll long long
using namespace std;
struct node1
{
int son[4],fail,len;
bool w;
}point[1510];
char s[105],str[105];
int p,n,dp[2][103][1510];
queue<int> myqueue;
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int len,i,j,h,k,t,num,ans1,ans2;
memset(point,0,sizeof(point));
scanf("%s%d%d",str,&p,&n);
num=0;
while (n--)
{
scanf("%s",s);
len=strlen(s);
h=0;
for (i=0;i<len;i++)
{
if (!point[h].son[s[i]-'a'])
{
point[h].son[s[i]-'a']=++num;
point[num].len=point[h].len+1;
}
h=point[h].son[s[i]-'a'];
if (point[h].w) break;
}
point[h].w=true;
}
while (!myqueue.empty()) myqueue.pop();
for (i=0;i<4;i++)
if (point[0].son[i]) myqueue.push(point[0].son[i]);
while (!myqueue.empty())
{
h=myqueue.front();
myqueue.pop();
if (point[point[h].fail].w) point[h].w=true;
if (point[h].w) continue;
for (i=0;i<4;i++)
{
k=point[h].fail;
while (k && !point[k].son[i]) k=point[k].fail;
point[point[h].son[i]].fail=point[k].son[i];
if (!point[h].son[i])
point[h].son[i]=point[k].son[i];
else
myqueue.push(point[h].son[i]);
}
}
h=0;
len=strlen(str);
for (i=0;i<len;i++)
{
while (h && !point[h].son[str[i]-'a']) h=point[h].fail;
h=point[h].son[str[i]-'a'];
if (point[h].w) break;
}
k=0;
memset(dp[k],0,sizeof(dp[k]));
ans1=ans2=0;
dp[k][len][h]=1;
if (point[h].w) ans2=1;
else
while (p--)
{
k=1-k;
memset(dp[k],0,sizeof(dp[k]));
for (i=0;i<=num;i++)
if (!point[i].w)
for (j=1;j<=100;j++)
if (dp[1-k][j][i])
{
if (j==1) ans1=(ans1+dp[1-k][j][i])%10007;
else
if (point[i].len>j-1)
{
t=point[i].fail;
dp[k][j-1][t]+=dp[1-k][j][i];
dp[k][j-1][t]%=10007;
}else
{
dp[k][j-1][i]+=dp[1-k][j][i];
dp[k][j-1][i]%=10007;
}
for (h=0;h<4;h++)
{
dp[k][j+1][point[i].son[h]]+=dp[1-k][j][i];
dp[k][j+1][point[i].son[h]]%=10007;
if (point[point[i].son[h]].w)
ans2=(ans2+dp[1-k][j][i])%10007;
}
}
}
printf("%d %d\n",ans1,ans2);
return 0;
}