题意:给定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; }