AC自动机专题:配合状压dp-HDU2825

    科技2024-12-03  17

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2825

    题目大意:

    一个长度为n( n ≤ 10 n \leq 10 n10)的序列。每个取值为 ‘a’ 到 ‘z’. 然后给你m( m ≤ 10 m \leq 10 m10)个长度小于10的字符串。问你至少含有k个不同字符串(不同字符串可能互相重叠覆盖)的序列的方案数.

    题目思路:

    不难想出状态: d p ( i , j , s t a t e s ) dp(i,j,states) dp(i,j,states)代表序列的前i位且在Trie图上的j节点并且生成的序列中字符串含有的状态为 s t a t e s states states的方案数。在利用刷表法(我为人人)即可.

    题目中提醒了,当字符串同时含有she 和 he 这种重叠字符串时,是要算两个的。所以这里在建AC自动机的时候要注意将危险标记传递下去。利用fail指针即可。(节点X的fail指针的含义就是以X为节点的前缀字符串的后缀集合链)

    AC代码:

    #include<bits/stdc++.h> using namespace std; const int maxn = 100 + 5; const int mod = 20090717; int n; int dp[30][105][1<<10]; namespace AC{ int tr[maxn][26] , tot; int fail[maxn] , S[maxn]; void init (){ tot = 0; for (int i = 0 ; i < maxn ; i++) { for (int j = 0 ; j < 26 ; j++){ tr[i][j] = 0; } fail[i] = S[i] = 0; } } void add (char * s , int g){ int u = 0; for (int i = 1 ; s[i] ; i++){ if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++tot; u = tr[u][s[i] - 'a']; } S[u] = (1 << (g - 1)); } queue<int>q; void build (){ while (q.size()) q.pop(); for (int i = 0 ; i < 26 ; i++) if (tr[0][i]) q.push(tr[0][i]); while (q.size()){ int u = q.front();q.pop(); for (int i = 0 ; i < 26 ; i++){ if (tr[u][i]){ fail[tr[u][i]] = tr[fail[u]][i]; q.push(tr[u][i]); }else { tr[u][i] = tr[fail[u]][i]; } } // 这里注意要传递危险节点状态 S[u] |= S[fail[u]]; } } } char s[maxn]; char tmp[250]; int cnt[1 << 10]; int main() { int n , m , t; cnt[0] = 0; for (int i = 1 ; i < (1 << 10) ; i++) cnt[i] = cnt[i >> 1] + (i & 1); while (scanf("%d%d%d" , &n , &m , &t) , n){ AC::init(); for (int i = 0 ; i <= n ; i++){ for (int j = 0 ; j <= 100 ; j++){ for (int k = 0 ; k < (1 << m) ; k++){ dp[i][j][k] = 0; } } } for (int i = 1 ; i <= m ; i++){ scanf("%s" , tmp + 1); AC::add(tmp , i); } AC::build(); dp[0][0][0] = 1; for (int i = 0 ; i < n ; i++){ for (int j = 0 ; j <= AC::tot ; j++){ for (int k = 0 ; k < (1 << m) ; k++){ if (!dp[i][j][k]) continue; for (int g = 0 ; g < 26 ; g++){ dp[i + 1][AC::tr[j][g]][ k | AC::S[AC::tr[j][g]] ] = (dp[i + 1][AC::tr[j][g]][ k | AC::S[AC::tr[j][g]] ] + dp[i][j][k]) % mod; } } } } int ans = 0; for (int i = 0 ; i <= AC::tot ; i++){ for (int k = 0 ; k < (1 << m) ; k ++){ if (cnt[k] < t) continue; // cout << i << " " << k << " " << dp[n][i][k] << endl; ans = (ans + dp[n][i][k]) % mod; } } printf("%d\n" , ans); } return 0; }
    Processed: 0.009, SQL: 8