题意 : 输入n(1<=n<=25)、m(0<=m<=10)、k,意思就是给你 m 个模式串,问你构建长度为 n 至少包含 k 个模式串的方案有多少种 mod20090717
直接进行计数一定是比较麻烦的,又发现m比较小,所以我们可以进行一下状态压缩再继续进行计算
记dp[i][k][s]为长度为i,在AC自动机上j位,拥有的模式串集合为s的方案数
此外AC自动机上每个点还有处理出来一个当前包含哪些模式串的集合
这个dp还要滚动一下数组,要不然会T!!
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,k,tot;
const int mod=20090717;
struct ac
{
int fail,son[26],end,state;
}tr[105];
int dp[2][105][1030];
void insert(char *ss,int cnt)
{
int u=0,len=strlen(ss);
for(int i=0;i<len;i++)
{
int dig=ss[i]-'a';
if(!tr[u].son[dig]) tr[u].son[dig]=++tot;
u=tr[u].son[dig];
}
tr[u].state|=(1<<cnt);
}
void build()
{
queue <int> q;
for(int i=0;i<26;i++) if(tr[0].son[i]) q.push(tr[0].son[i]);
while(!q.empty())
{
int u=q.front(); q.pop();
tr[u].state|=tr[tr[u].fail].state;
for(int i=0;i<26;i++)
{
if(tr[u].son[i])
{
tr[tr[u].son[i]].fail=tr[tr[u].fail].son[i];
q.push(tr[u].son[i]);
}
else tr[u].son[i]=tr[tr[u].fail].son[i];
}
}
}
int check(int x)
{
int res=0;
for(int i=0;i<n;i++)
if(x&(1<<i))
res++;
if(res>=k) return 1;
return 0;
}
int DP()
{
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
int cur=1;
for(int i=0;i<n;i++)
{
memset(dp[cur],0,sizeof(dp[cur]));
for(int j=0;j<=tot;j++)
for(int st=0;st<(1<<m);st++)
if(dp[cur^1][j][st])
for(int dig=0;dig<26;dig++)
{
int to=tr[j].son[dig];
dp[cur][to][st|tr[to].state]=(dp[cur][to][st|tr[to].state]+dp[cur^1][j][st])%mod;
}
cur^=1;
}
int ans=0;
for(int st=0;st<(1<<m);st++)
if(check(st)==1)
{
for(int i=0;i<=tot;i++)
ans=(ans+dp[cur^1][i][st])%mod;
}
return ans;
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
while(scanf("%d%d%d",&n,&m,&k) && (n || m || k))
{
memset(tr,0,sizeof(tr));
tot=0; char s[11];
for(int i=1;i<=m;i++)
{
scanf("%s",s);
insert(s,i-1);
}
build();
printf("%d\n",DP());
}
return 0;
}