题目链接: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
aa
bb
icpc
hello
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原创,但可以转载,因为我们是兄弟。