做的第一道状压加AC。真是各种悲剧……先是状态设计搓了MLE,然后因为递归TLE,看了题解改成迭代递推又WA。然后发现AC自动机最为重要的一点被我忽略了,就是节点标记的传递……改了之后才AC。
其实这题算是比较裸的一道AC自动机加DP,状态很自然就可以想到。
用dp[i][j][k]表示已经过i个结点,位于j结点,所达到的状态(即所包含的字串)。
状态方程为:dp[i+1][ch][k|st]+=dp[i][j][k]。
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
typedef int LL;
const LL mod=20090717;
const int sigma=26;
int n,m,K;
char s[20][20];
int ch[210][sigma],val[210],f[210];
int sz;
int idx(char c){return c-'a';}
void init(){sz=1;memset(ch[0],0,sizeof(ch[0]));}
void insert(char* T,int v)
{
int l=strlen(T),u=0;
for(int i=0;i<l;i++)
{
int c=idx(T[i]);
if(!ch[u][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]|=(1<<v);
}
void getfail()
{
queue<int> q;
f[0]=0;
for(int i=0;i<sigma;i++)
{
int u=ch[0][i];
if(u)
{
f[u]=0;
q.push(u);
}
}
while(!q.empty())
{
int r=q.front();q.pop();
for(int i=0;i<sigma;i++)
{
int u=ch[r][i];
if(!u)
{
ch[r][i]=ch[f[r]][i];
continue;
}
q.push(u);
int v=f[r];
while(v&&!ch[v][i]) v=f[v];
f[u]=ch[v][i];
val[u]|=val[f[u]];
}
}
}
LL dp[30][120][1200];
int bitcount(int x){return x==0?0:bitcount(x/2)+(x&1);}
int main()
{
freopen("in.txt","r",stdin);
while(~scanf("%d%d%d",&n,&m,&K))
{
if(!n&&!m&&!K) break;
init();
for(int i=0;i<m;i++)
{
scanf("%s",s[i]);
insert(s[i],i);
}
getfail();
for(int i=0;i<=n;i++)
for(int j=0;j<sz;j++)
for(int k=0;k<(1<<m);k++)
dp[i][j][k]=0;
dp[0][0][0]=1;
for(int i=0;i<n;i++)
for(int j=0;j<sz;j++)
for(int k=0;k<(1<<m);k++)
{
if(dp[i][j][k]==0) continue;
for(int t=0;t<sigma;t++)
{
int v=ch[j][t];
int st=val[v];
dp[i+1][v][k|st]=(dp[i][j][k]+dp[i+1][v][k|st])%mod;
}
}
int ans=0;
for(int i=0;i<sz;i++)
for(int k=0;k<(1<<m);k++)
if(bitcount(k)>=K) ans=(ans+dp[n][i][k])%mod;
printf("%d\n",ans);
}
return 0;
}