题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2825
题目大意:给定m个字符串,求长度为n且至少含有k个字符串(k个字符串全部都是m里的字符串)的方案数,模以20090717。
解题思路:这题并不难,只是常规的ac自动机+状态压缩DP,状态转移方程比较容易想。dp[i][j][k]表示长度为i且在ac自动机上的位置是j,包含的字符串的字符串集合二进制表示为k.状态转移方程为:dp[i+1][j->next->pos][k|j->next->cnt] += dp[i][j][k(j->next表示下一个节点,pos为位置,cnt为含有的字符串集合)。ac自动机上的状态很明显,很容易和dp结合,较为综合。
测试数据:
4 2 2
bb
icpc
world
10 0 0
代码:
#include <stdio.h> #include <string.h> #define MIN 1200 #define MAX 20000 #define MOD 20090717 struct node { int in,cnt; node *fail,*next[26]; }*root,arr[MAX],*q[MAX]; char dir[20][20]; int head,tail; int n,m,p,total,bit[MIN]; int dp[30][120][MIN],ans; node *CreateNode() { node *p = &arr[total]; p->cnt = 0; p->in = total++; p->fail = NULL; for (int i = 0; i < 26; ++i) p->next[i] = NULL; return p; } void Initial() { ans = total = 0; root = CreateNode(); for (int i = 0; i < (1<<m); ++i)//计算含有的1的个数 bit[i] = bit[i>>1] + (i & 1); } void Insert(char *str,int in) { int i = 0,k; node *p = root; while (str[i]) { k = str[i++] - 'a'; if (p->next[k] == NULL) p->next[k] = CreateNode(); p = p->next[k]; } p->cnt |= (1 << in); } void Build_AC(){ head = 0,tail = 0; q[head++] = root; root->fail = root; while (head > tail) { node *p = q[tail++]; for (int i = 0; i < 26; ++i) { if (p->next[i] != NULL) { //添加fail指针 if (p == root) p->next[i]->fail = root; else p->next[i]->fail = p->fail->next[i]; p->next[i]->cnt |= p->fail->next[i]->cnt; q[head++] = p->next[i]; } else { //添加虚节点 if (p == root) p->next[i] = root; else p->next[i] = p->fail->next[i]; } } } } int Solve(){ int i,j,k,st,t,s,tp; for (i = 0; i <= n; ++i) for (j = 0; j < total; ++j) for (k = 0; k < (1<<m); ++k) dp[i][j][k] = 0; dp[0][0][0] = 1; for (i = 0; i < n; ++i) for (j = 0; j < total; ++j) for (k = 0; k < (1<<m); ++k) { if (dp[i][j][k] == 0) continue; tp = dp[i][j][k]; for (t = 0; t < 26; ++t) { s = arr[j].next[t]->in; st = arr[j].next[t]->cnt; //状态转移 dp[i+1][s][st|k] = (tp + dp[i+1][s][st|k]) % MOD; } } for (j = 0; j < total; ++j) for (k = 0; k < (1<<m); ++k) if (bit[k] >= p) ans = (ans + dp[n][j][k]) % MOD; return ans; } int main() { int i,j,k; while (scanf("%d%d%d",&n,&m,&p)) { if (n + m + p == 0) break; Initial(); for (i = 0; i < m; ++i) scanf("%s",dir[i]),Insert(dir[i],i); Build_AC(); int ans = Solve(); printf("%d\n",ans); } return 0; }
本文ZeroClock原创,但可以转载,因为我们是兄弟。