题意:给n个长度为m的单词串,问你一段长度为m的文本中包含任一单词串的方案数,对10007取模
n <= 60, m <= 100
思路:单词串建个AC自动机,在AC自动机上跑DP
一共有26^m种方案,减去不包含有单词串的方案就是答案
设dp[i][j]为第i个字符,在AC自动机上第j个节点的方案数,答案是26^m-sum(dp[m])
转移就是通过前一个字符的fail指针转移到后一个
如果前一个状态包含单词串,那么后一个无论是什么都包含
同样如果有走到结束标记的点也会导致包含
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<queue> 6 #define LL long long 7 #define debug(x) cout << "[" << x << "]" << endl 8 using namespace std; 9 10 const int mx = 6010; 11 const int mod = 10007; 12 int ans = 1; 13 int dp[610][mx]; 14 char s[110]; 15 16 struct Trie{ 17 int nxt[mx][26], sum[mx], fail[mx]; 18 int cnt; 19 20 void insert(const char* s){ 21 int u = 0, len = strlen(s); 22 for (int i = 0; i < len; i++){ 23 int c = s[i]-'A'; 24 if (!nxt[u][c]) nxt[u][c] = ++cnt; 25 u = nxt[u][c]; 26 } 27 sum[u] = 1; 28 } 29 void build(){ 30 queue<int> q; 31 for (int i = 0; i < 26; i++) 32 if (nxt[0][i]) fail[nxt[0][i]] = 0, q.push(nxt[0][i]); 33 while (!q.empty()){ 34 int u = q.front(); q.pop(); 35 for (int i = 0; i < 26; i++){ 36 if (nxt[u][i]) fail[nxt[u][i]] = nxt[fail[u]][i], q.push(nxt[u][i]); 37 else nxt[u][i] = nxt[fail[u]][i]; 38 } 39 if (sum[fail[u]]) sum[u] = 1; 40 } 41 } 42 void f(int i){ 43 for (int j = 0; j <= cnt; j++){ 44 if (sum[j] || !dp[i-1][j]) continue; 45 for (int k = 0; k < 26; k++){ 46 int c = nxt[j][k]; 47 dp[i][c] = (dp[i][c]+dp[i-1][j])%mod; 48 } 49 } 50 } 51 }ac; 52 53 int main(){ 54 int n, m; 55 scanf("%d%d", &n, &m); 56 for (int i = 1; i <= n; i++){ 57 scanf("%s", s); 58 ac.insert(s); 59 } 60 ac.build(); 61 dp[0][0] = 1; 62 for (int i = 1; i <= m; i++) ac.f(i); 63 for (int i = 1; i <= m; i++) ans = ans*26%mod; 64 for (int i = 0; i <= ac.cnt; i++) 65 if (!ac.sum[i]) ans = (ans+mod-dp[m][i])%mod; 66 printf("%d ", ans); 67 return 0; 68 }