hdu 2825 Wireless Password

Wireless Password


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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章