HDU 2825 AC自动机+DP
题意:一个密码,长度为 n,然后有m个magic words,这个密码至少由k个magic words组成。
问这个密码可能出现的总数。
思路:首先构造AC自动机,由于m很小,才10 ,我们可以使用二进制来表示每个magic words的使用情况。
对于dp[i][j][k],表示长度为i 时,匹配到j这个节点,当前选取的magic words是k 状态时的最大数量。
#include <set> #include <map> #include <stack> #include <cmath> #include <queue> #include <cstdio> #include <string> #include <vector> #include <iomanip> #include <cstring> #include <iostream> #include <algorithm> #define Max 2505 #define FI first #define SE second #define ll long long #define PI acos(-1.0) #define inf 0x3fffffff #define LL(x) ( x << 1 ) #define bug puts("here") #define PII pair<int,int> #define RR(x) ( x << 1 | 1 ) #define mp(a,b) make_pair(a,b) #define mem(a,b) memset(a,b,sizeof(a)) #define REP(i,s,t) for( int i = ( s ) ; i <= ( t ) ; ++ i ) using namespace std; #define MOD 20090717 #define N 1111111 int n , m , k ; int cnt ; struct AC_AUTO { int next[26] ; int fail ; int st ; void init() { mem(next ,0) ; fail = -1 ; st = 0 ; } } a[500000]; int vis[111111] ; void show(int now) { vis[now] = 1 ; cout << now << " " << a[now].fail << endl; for (int i = 0 ; i < 26 ; i ++ ) { if(a[now].next[i] != 0 && !vis[a[now].next[i]]) { show(a[now].next[i]) ; } } } void insert(char *s,int k) { int p = 0 ; for(int i = 0 ; s[i] ; i ++) { int t = s[i] - 'a' ; if(a[p].next[t] == 0) { a[cnt].init() ; a[p].next[t] = cnt ++ ; } p = a[p].next[t] ; } a[p].st |= (1 << k) ; } int q[111111] ; void ac_bfs() { int i,head = 0,tail = 0; q[tail ++]=0; while(head < tail) { int front = q[head ++]; for(i = 0; i < 26 ; i ++) { if(a[front].next[i] == 0) {/// if(front == 0)a[front].next[i] = 0 ; else a[front].next[i] = a[a[front].fail].next[i] ; } else { int p = a[front].fail ; while(p != -1) { if(a[p].next[i] != 0) { a[a[front].next[i]].fail = a[p].next[i] ; a[a[front].next[i]].st |= a[a[p].next[i]].st ; break ; } p = a[p].fail ; } if(p == -1)a[a[front].next[i]].fail = 0 ; q[tail ++] = a[front].next[i] ; } } } } int dp[26][200][1 << 10] ; int solve() { for (int i = 0 ; i <= n ; i ++ ) for (int j = 0 ; j <= cnt ; j ++ ) for (int x = 0 ; x <= 1 << m ; x ++ ) dp[i][j][x] = 0 ; dp[0][0][0] = 1 ; for (int i = 0 ; i < n ; i ++ )//长度为i时 for (int j = 0 ; j < cnt ; j ++ )//在第j个节点 for (int x = 0 ; x < 1 << m ; x ++) { //第x个状态 if(!dp[i][j][x])continue ; for (int y = 0 ; y < 26 ; y ++ ) { //字母y int newj = a[j].next[y] ; int newst = x | a[newj].st ; dp[i + 1][newj][newst] = (dp[i][j][x] + dp[i + 1][newj][newst] ) % MOD ; } } int ans = 0 ; for (int i = 0 ; i < 1 << m ; i ++ ) { int ret = 0 ; int d = i ; for (; d ; d -= d & (-d) , ret ++) ; if(ret < k )continue ; for (int j = 0 ; j < cnt ; j ++ ) { ans = (ans + dp[n][j][i]) % MOD ; } } return ans ; } char in[111] ; int main() { while(cin >> n >> m >> k, (n + m + k)) { a[0].init() ; cnt = 1 ; for (int i = 0 ; i < m ; i ++ ) { scanf("%s",in) ; insert(in , i) ; } ac_bfs() ; printf("%d\n",solve()) ; } return 0 ; }