HDU 2825 Wireless Password(AC自动机 + 状压DP)题解

时间:2023-03-09 00:35:44
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;
}