题目大意:给定若干个字符串,问长度为m并且至少包含一个之前给定的字符串的字符串有几种?
题目解析:考虑补集,dp[i][j]为当前第i位,停留在第j个tire节点上的数目,转移的话看下一个字符存不存在,不存在就一直找fai节点,注意danger;
#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 10007
using namespace std;
int n,m,sz=1,ans1,ans2=1;
int tire[6001][27],fail[6001],q[6001],dp[101][6001];
char s[101];
bool danger[6001];
void init()
{
memset(dp,0,sizeof(dp));
memset(tire,0,sizeof(tire));
memset(danger,false,sizeof(danger));
for(int i=1;i<=26;i++)tire[0][i]=1;
}
void ins()
{
int now=1,c;
for(int i=0;i<strlen(s);i++)
{
c=s[i]-'A'+1;
if(tire[now][c])now=tire[now][c];
else now=tire[now][c]=++sz;
}
danger[now]=1;
}
void acmach()
{
int t=0,w=1,now;
q[0]=1;fail[1]=0;
while(t<w)
{
now=q[t++];
for(int i=1;i<=26;i++)
{
if(!tire[now][i])continue;
int k=fail[now];
while(!tire[k][i])k=fail[k];
fail[tire[now][i]]=tire[k][i];
if(danger[tire[k][i]])
danger[tire[now][i]]=1;
q[w++]=tire[now][i];
}
}
}
void solve(int x)
{
for(int i=1;i<=sz;i++)
{
if(danger[i]||!dp[x-1][i])continue;
for(int j=1;j<=26;j++)
{
int k=i;
while(!tire[k][j])k=fail[k];
dp[x][tire[k][j]]=(dp[x][tire[k][j]]+dp[x-1][i])%mod;
}
}
}
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
ins();
}
acmach();
dp[0][1]=1;
for(int i=1;i<=m;i++)
solve(i);
for(int i=1;i<=m;i++)
ans2=(ans2*26)%mod;
for(int i=1;i<=sz;i++)
if(!danger[i])ans1=(ans1+dp[m][i])%mod;
printf("%d",(ans2-ans1+mod)%mod);
return 0;
}