HDU 2825 Wireless Password(AC自动机 + 状压DP)题解
题意:m个密码串,问你长度为n的至少含有k个不同密码串的密码有几个
思路:状压一下,在build的时候处理fail的时候要用 | 把所有的后缀都加上。
代码:
#include<cmath> #include<set> #include<map> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 100 + 5; const int M = 50 + 5; const ull seed = 131; const double INF = 1e20; const int MOD = 20090717; int n, m, K; int dp[28][maxn][1100]; int num[1100]; struct Aho{ struct state{ int next[26]; int fail, cnt; }node[maxn]; int size; queue<int> q; void init(){ size = 0; newtrie(); while(!q.empty()) q.pop(); } int newtrie(){ memset(node[size].next, 0, sizeof(node[size].next)); node[size].cnt = node[size].fail = 0; return size++; } void insert(char *s, int id){ int len = strlen(s); int now = 0; for(int i = 0; i < len; i++){ int c = s[i] - 'a'; if(node[now].next[c] == 0){ node[now].next[c] = newtrie(); } now = node[now].next[c]; } node[now].cnt = 1 << id; } void build(){ node[0].fail = -1; q.push(0); while(!q.empty()){ int u = q.front(); q.pop(); if(node[node[u].fail].cnt && u) node[u].cnt |= node[node[u].fail].cnt; for(int i = 0; i < 26; i++){ if(!node[u].next[i]){ if(u == 0) node[u].next[i] = 0; else node[u].next[i] = node[node[u].fail].next[i]; } else{ if(u == 0) node[node[u].next[i]].fail = 0; else{ int v = node[u].fail; while(v != -1){ if(node[v].next[i]){ node[node[u].next[i]].fail = node[v].next[i]; break; } v = node[v].fail; } if(v == -1) node[node[u].next[i]].fail = 0; } q.push(node[u].next[i]); } } } } void query(){ for(int i = 0; i <= n; i++){ for(int j = 0; j < size; j++){ for(int k = 0; k < (1 << m); k++){ dp[i][j][k] = 0; } } } for(int i = 0; i < 26; i++){ if(node[node[0].next[i]].cnt){ int v = node[node[0].next[i]].cnt; dp[1][node[0].next[i]][v]++; // printf("* %d %d %d\n", 1, node[0].next[i], v); } else dp[1][node[0].next[i]][0]++; } for(int i = 1; i < n; i++){ for(int j = 0; j < size; j++){ for(int k = 0; k < (1 << m); k++){ if(dp[i][j][k] == 0) continue; for(int l = 0; l < 26; l++){ if(node[node[j].next[l]].cnt){ int v = node[node[j].next[l]].cnt; dp[i + 1][node[j].next[l]][k | v] = (dp[i + 1][node[j].next[l]][k | v] + dp[i][j][k]) % MOD; } else{ dp[i + 1][node[j].next[l]][k] = (dp[i + 1][node[j].next[l]][k] + dp[i][j][k]) % MOD; } } } } } int ans = 0; for(int i = 0; i < size; i++){ for(int j = 0; j < (1 << m); j++){ if(num[j] >= K) ans = (ans + dp[n][i][j]) % MOD; } } printf("%d\n", ans); } }ac; char s[100]; int main(){ for(int i = 0; i < 1100; i++){ int temp = 0, x = i; while(x){ temp += x & 1; x >>= 1; } num[i] = temp; } while(~scanf("%d%d%d", &n, &m, &K) && n + m + K){ ac.init(); for(int i = 0; i < m; i++){ scanf("%s", s); ac.insert(s, i); } ac.build(); ac.query(); } return 0; }