1.题目链接。给定n个字符串,让你构造一个由m个字符组成的字符串并且至少包含前n个字符串中的一个,有多少种构造方案。
2.首先是一个多模式匹配的问题,对输入的字符串建立AC自动机,然后开始DP。由于正面考虑有很多种情况,我们发面考虑,不含他们中的任意一个。dp[i][j]:考虑第i位字符当前位置在AC自动机上的第j个节点时,不包含任意一个字符串的方案书。然后向i+1转移,转移时,我们需要枚举j的儿子k,对于k我们判断是不是可以把它放在第i+1个位置,显然,我们需要直到加入k之后,是不是会让前边的东西出现n个单词中的一种,这个时候,我们从当前节点,沿着fail指针向上跳,如果发现有节点的有k儿子,并且标记是真,那么k不能被加入,否则可以被加入,如果可以被加入,那么找到具有这个k儿子但是没有标记的地方(找的方式依然是沿着fail指针向上跳),那么dp[i][j]将会更新这个地方的值。最后的答案就是考虑m个字符,所有节点值的和。然后总方案数减去就是答案。
#include<bits/stdc++.h>
#define md 10007
#define ll long long
using namespace std;
char ch[1000];
int fail[6010], dp[1200][6010];
int tr[6010][30], trcnt, danger[6010], n, m, q[6010];
void insert()
{
int now = 1, len = strlen(ch + 1);
for (int k = 1; k <= len; k++)
{
int y = ch[k] - 'A' + 1;
if (tr[now][y]) now = tr[now][y];
else now = tr[now][y] = ++trcnt;
}
danger[now] = 1;
}
void acmach()
{
int head = 0, tail = 0, now;
q[++tail] = 1;
while (head < tail)
{
now = q[++head];
for (int k = 1; k <= 26; k++)
if (tr[now][k])
{
int y = fail[now];
while (!tr[y][k]) y = fail[y];
fail[tr[now][k]] = tr[y][k];
q[++tail] = tr[now][k];
}
}
}
ll qpow(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)res = res * a % md;
a = a * a % md;
b >>= 1;
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i <= 26; i++)tr[0][i] = 1;
trcnt = 1;
for (int i = 1; i <= n; i++)
{
scanf("%s", ch + 1);
insert();
}
acmach();
dp[0][1] = 1;
for (int i = 0; i <= m - 1; i++)
{
for (int j = 1; j <= trcnt; j++)
{
for (int k = 1; k <= 26; k++)
{
int now = j;
int flag = 0;
while (now)
{
if (danger[tr[now][k]] == 1)
{
flag = 1;
break;
}
now = fail[now];
}
if (flag == 1)continue;
now = j;
while (!tr[now][k])now = fail[now];
now = tr[now][k];
dp[i + 1][now] = (dp[i + 1][now] + dp[i][j]) % md;
}
}
}
ll res = qpow(26, m);
ll ans = 0;
for (int i = 1; i <= trcnt; i++)
{
ans = ans + dp[m][i];
ans %= md;
}
ans = (res - ans + md) % md;
cout << ans << endl;
}