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;
}