AC自动机的DP...每个节点是状态..每条边是转移方向..其实这题和 POJ-2778 DNA Sequence 是一回事..只是这题是高精度..并且数据范围没那么大..所以使得直接DP的效率从时间和空间上都远远高于了用矩阵乘法...囧..
其实准确的说这题所构造的图不是AC自动机而是Trie图...为了DP转移时更加方便..把节点没有的孩子赋值为其fail点的这个孩子...这样在DP时就不用考虑fail了..直接枚举每个点来通过这个点的有向边更新其他点...
DP时很明显每一次都只于前一次有关...所以可以用滚动数组来存点的dp值..
DP转移方程为:
v[k][x]+=v[1-k][h]; (k做滚动数组标记的..h是有向线段的起点..x是有向线段的终点)
这题很显然要用高精度了..因为是这样的转移关系..所以只要考虑在做加法的时候会不会爆..我是每位存8个十进制数...效率确实很好....
这题非常要小心的一个是对带病毒点的处理...还有就是对结果是0的处理...我就是处理得不彻底结果WA了N久....
我的一些测试数据(也贴在POJ-1625的Discuss了..):
50 50 10
qwertyuiop[]\asdfghjkl;'zxcvbnm,./ QWERTYUIOP{}|AS
aegaegu
etoijqt
tquqi
witowwt
zxcjnc
oeit
potieq
iojge
nvoq
piqper
ans=8881647922834867244791415981705771412427494861672253136057167374729235842468240763290
1 1 1
a
a
ans=0
5 10 3
abcde
abc
bc
c
ans=1048576
Program:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<queue>
#define uchar unsigned char
#define N 55
using namespace std;
struct node1
{
int son[55],fail;
bool w;
}point[202];
int n,m,p,v[2][202][N+2],g[606];
char s[105];
bool used[202];
queue<int> myqueue;
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int i,l,a[N+2],num,h,k;
scanf("%d%d%d\n",&n,&m,&p);
gets(s); l=strlen(s);
memset(g,0,sizeof(g));
for (i=0;i<l;i++) g[s[i]+128]=i;
memset(point,0,sizeof(point));
num=0;
while (p--)
{
gets(s);
l=strlen(s);
h=0;
for (i=0;i<l;i++)
{
k=g[s[i]+128];
if (!point[h].son[k])
{
num++;
point[h].son[k]=num;
}
h=point[h].son[k];
if (point[h].w) break;
}
point[h].w=true;
}
while (!myqueue.empty()) myqueue.pop();
memset(v,0,sizeof(v));
for (i=0;i<n;i++)
if (point[0].son[i])
{
myqueue.push(point[0].son[i]);
if (!point[point[0].son[i]].w)
v[0][point[0].son[i]][0]++;
}else v[0][0][0]++;
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<n;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]);
}
}
k=0;
m--;
while (m--)
{
k=1-k;
memset(v[k],0,sizeof(v[k]));
memset(used,false,sizeof(used));
used[0]=true;
myqueue.push(0);
while (!myqueue.empty())
{
h=myqueue.front();
myqueue.pop();
if (point[h].w) continue;
for (i=0;i<n;i++)
if (!point[point[h].son[i]].w)
{
if (!used[point[h].son[i]])
{
myqueue.push(point[h].son[i]);
used[point[h].son[i]]=true;
}
for (p=0;p<N;p++)
{
v[k][point[h].son[i]][p]+=v[1-k][h][p];
if (v[k][point[h].son[i]][p]>99999999)
{
v[k][point[h].son[i]][p+1]+=v[k][point[h].son[i]][p]/100000000;
v[k][point[h].son[i]][p]%=100000000;
}
}
}
}
}
memset(a,0,sizeof(a));
for (i=0;i<=num;i++)
for (p=0;p<N;p++)
{
a[p]+=v[k][i][p];
if (a[p]>99999999)
{
a[p+1]+=a[p]/100000000;
a[p]%=100000000;
}
}
for (p=N;p>=0;p--)
if (a[p]) break;
if (p==-1) p=0;
printf("%d",a[p]);
p--;
for (;p>=0;p--)
{
if (a[p]<10000000 ) printf("0");
if (a[p]<1000000 ) printf("0");
if (a[p]<100000 ) printf("0");
if (a[p]<10000 ) printf("0");
if (a[p]<1000 ) printf("0");
if (a[p]<100 ) printf("0");
if (a[p]<10 ) printf("0");
printf("%d",a[p]);
}
printf("\n");
return 0;
}