AC 自动机+ DP
题目意思:给定M个子串,现需要构造一个长度为N的串使得该串中至少包含K个子串,求有多少中构造方法。开始的时候错误的理解题目意思:认为只需要包含k个字串(允许重复)。。。现在状态就很容易写了。dp[i][j][k] 长度为i处于j状态的包含的字串的情况为k的可能串的个数, 方程就可以直接得到了,这里就不写了,参看程序。。
/*
author : csuchenan
algorithm : AC自动机+DP
2012-11-01 13:41:39 Accepted 2825 390MS 6764K 3172 B C++ csu_chenan
*/
#include <cstdio>
#include <cstring>
#include <queue>
using std::queue;
const int maxn = 105;
const int MAX_SIZE = 26;
const int mod = 20090717;
struct Trie{
int next[MAX_SIZE];
int fail, tag;
void init(){
memset(next, 0, sizeof(next));
fail = 0;
tag = 0;
}
}trie[maxn];
int cnt, n, m, k, r;
int dp[30][maxn][1030];
int map[1030];
void insert(char * str, int root, int s){
int i;
while(*str){
i = (*str ++) - 'a';
if(trie[root].next[i]==0){
trie[++cnt].init();
trie[root].next[i] = cnt;
}
root = trie[root].next[i];
}
trie[root].tag |= (1<<s);
}
void build_ac(int root){
queue<int> Q;
Q.push(root);
trie[root].fail = root;
while(!Q.empty()){
int cur = Q.front();
Q.pop();
for(int i = 0; i < MAX_SIZE; i ++){
int child = trie[cur].next[i];
if(child!=0){
Q.push(child);
if(cur == root)
trie[child].fail = root;
else{
int tmp = trie[cur].fail;
trie[child].fail = trie[tmp].next[i];
trie[child].tag |= trie[ trie[tmp].next[i] ].tag;
}
}
else{
if(cur==root){
trie[cur].next[i] = root;
}
else{
int tmp = trie[cur].fail;
trie[cur].next[i] = trie[tmp].next[i];
}
}
}
}
}
int main(){
for(int i = 0; i <= 1024; i ++){
map[i] = map[i>>1] + (i&1);
}
while(scanf("%d%d%d", &n, &m, &k)!=EOF, n||m||k){
char str[20];
r = cnt = 0;
trie[r].init();
for(int i = 0; i < m; i ++){
scanf("%s", str);
insert(str, r, i);
}
build_ac(r);
int mx = 1<<m;
//这里最好不要使用memset,时间会慢很多,并且跑出来的内存大很多
//直接数组赋值,速度快了将近一倍,内存也少了很多
for(int i = 0; i <= n; i ++){
for(int j = 0; j <= cnt; j ++){
for(int t = 0; t < mx; t ++)
dp[i][j][t] = 0;
}
}
dp[0][0][0] = 1;
for(int i = 0; i < n; i ++){
for(int j = 0; j <= cnt; j ++){
for(int s = 0; s < mx; s ++){
if(dp[i][j][s]==0)
continue;
for(int p = 0; p < MAX_SIZE; p ++){
//这个地方要判断当前状态s是否合法即,当从j状态走过的时候,trie[j].tag
//是否被包含。。
if((s&trie[j].tag) != trie[j].tag){
continue;
}
int cur = trie[j].next[p];
int tag = s | trie[cur].tag;
dp[i+1][cur][tag] = (dp[i+1][cur][tag] + dp[i][j][s])%mod;
}
}
}
}
int ans = 0;
for(int i = 0; i <= cnt; i ++){
for(int j = 0; j < mx; j ++){
if(map[j] >= k){
ans = (ans + dp[n][i][j])%mod;
}
}
}
printf("%d\n", ans%mod);
}
return 0;
}