Description
随机生成一篇文章,如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。
现在给出你使用者了解的N个单词,随机生成长度为M的文章,求有多少个文章是可读的。
Sample Input
2 2
A
B
Sample Output
100
这题好题啊。。。
首先考虑DP,设f[i][j]为到第i位匹配到AC机上编号为j的位置的可读文章数。
对于状态f[i-1][j]考虑它可以更新什么状态。
对于当前i,j,枚举一遍1~26,对于每个字符都跳fail,跳到可继承的位置gg。
则可得DP方程为:f[i][gg]+=f[i-1][j]。
#include <cstdio>
#include <cstring>
using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
const int maxn = 110;
const int mod = 10007;
struct node {
int fail, v[30];
node() {memset(v, -1, sizeof(v));}
} t[maxn * 60]; int cnt, list[maxn * 60];
int cc[maxn * 60], f[maxn][60 * maxn];
char ss[maxn];
void bt() {
int x = 0;
int len = strlen(ss + 1);
for(int i = 1; i <= len; i++) {
int y = ss[i] - 'A' + 1;
if(t[x].v[y] == -1) t[x].v[y] = ++cnt;
x = t[x].v[y];
}
cc[x] = 1;
}
void get_fail() {
int head = 1, tail = 2;
list[1] = 0;
while(head != tail) {
int x = list[head];
for(int i = 1; i <= 26; i++) {
int y = t[x].v[i];
if(y == -1) continue;
if(x == 0) t[y].fail = 0;
else {
int j = t[x].fail;
while(j && t[j].v[i] == -1) j = t[j].fail;
t[y].fail = _max(0, t[j].v[i]);
}
list[tail++] = y;
}
if(cc[t[x].fail]) cc[x] = 1;
head++;
}
}
int main() {
int n, m; scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%s", ss + 1);
bt();
}
get_fail();
f[0][0] = 1;
int kk = 1;
for(int i = 1; i <= m; i++) {
(kk *= 26) %= mod;
for(int j = 0; j <= cnt; j++) {
if(cc[j] || !f[i - 1][j]) continue;
for(int k = 1; k <= 26; k++) {
int u = j;
while(u && t[u].v[k] == -1) u = t[u].fail;
if(t[u].v[k] == -1) (f[i][0] += f[i - 1][j]) %= mod;
else (f[i][t[u].v[k]] += f[i - 1][j]) %= mod;
}
}
}
int ans = 0;
for(int i = 0; i <= cnt; i++) if(!cc[i]) (ans += f[m][i]) %= mod;
(ans = kk - ans) %= mod;
printf("%d\n", (ans + mod) % mod);
return 0;
}