BZOJ 1030 AC自动机+DP

题意:给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\n", ans);
67     return 0;
68 }

 

posted @ 2018-10-07 18:52  QAQorz  阅读(145)  评论(0编辑  收藏