题意:给定n个字符串,要求构造长度为m的字符串使其包含至少一个给定字符串,求方案数。
AC自动机+DP肯定是显然的啦。但是要求“至少一个”被包含肯定不好搞,所以我们反过来想,用总方案数-一个也不包含的方案数这样就好搞多了。具体算法流程如下:
1.对n个字符串建立AC自动机;
2.DP,f[i][j]表示字符串构造到第i位,在AC自动机上跑到j节点时的方案数。转移方程:(如果tr[j][k]不是一个字符串的末尾)f[i+1][tr[j][k]]=(f[i+1][tr[j][k]]+f[i][j])%MOD,其中k是26个字母。
3.ans=26^m-∑f[m][i],其中i是所有AC自动机上的节点编号。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<queue>
#define ll long long
#define INF 0x3f3f3f3f
#define N 110
#define MOD 10007
using namespace std;
char str[N];
int n;
int tot=1,len,tr[N*180][30],g[N*180];
bool num[180*N];
int f[N][N*180];
queue<int> q;
void add(int x,int y){
if(y>=len){ num[x]=1; return;}
int tmp=str[y]-'A';
if(!tr[x][tmp]) tr[x][tmp]=++tot;
add(tr[x][tmp],y+1);
}
void bfs(){
int j,y;
g[1]=0;
q.push(1);
while(q.size()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
for(j=g[u];j;j=g[j]) if(tr[j][i]) break;
if(y=tr[u][i]){
g[y]=j ? tr[j][i] : 1;
num[y]|=num[g[y]];
q.push(y);
}
else tr[u][i]=j ? tr[j][i] : 1;
}
}
}
void dp(){
f[0][1]=1;
for(int i=0;i<len;i++){
for(int j=1;j<=tot;j++){
for(int k=0;k<26;k++)
if(!num[tr[j][k]]){
f[i+1][tr[j][k]]=(f[i][j]+f[i+1][tr[j][k]])%MOD;
}
}
}
}
void work(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",str);
len=strlen(str);
add(1,0);
}
len=m;
bfs();
dp();
int ans=1;
for(int i=1;i<=m;i++) ans=ans*26%MOD;
for(int i=1;i<=tot;i++) {
ans=ans-f[len][i];
if(ans<0) ans+=MOD;
}
printf("%d\n",ans);
}
int main(){
work();
return 0;
}