1. 程式人生 > >1076 || LA 4126 Password Suspects (AC自動機 + 狀壓DP + 列印解)

1076 || LA 4126 Password Suspects (AC自動機 + 狀壓DP + 列印解)

題意:

讓你構造一個長度為n 的串, 告訴你m個串, 要求長度為n 的串 必須包含m 個串, 問你有多少種方案。如果方案數 <= 42 輸出所有解。

思路:

不看輸出方案。

很明顯一個自動機  + 狀壓的題目。

令dp[i][j][k]  表示 構造字串的第i 位, 目前在自動機的j 結點, 包含m 個串的狀態為k 的方案數。那麼直接轉移就好了。

但是要列印解 。

 感覺這裡比較亂 想了好久。

因為自己寫的dp 都是正著推的, 從dp[0][0][0] 推到終點。 但是這樣不好列印解。

於是改成了記憶話搜尋, 從終點向前推。

這樣直接從起點 在dfs 一遍就能列印解了, 直接看當前的dp 值是不是大於0 即可, 大於0 就繼續遞迴 ,否則return。

注意:

可能會有重複單詞的出現。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
using namespace std;

const int maxn = 100 + 2;

struct Trie{
    int L, root;
    int next[maxn][26];
    int fail[maxn];
    int flag[maxn];
    long long dp[26][maxn][ (1<<10)+2 ];

    void init(){
        L = 0;
        root = newnode();
    }

    int newnode(){
        for (int i = 0; i < 26; ++i){
            next[L][i] = -1;
        }
        flag[L] = 0;
        return L++;
    }


    void insert(char* s,int ii){
        int len = strlen(s);
        int nod = root;
        for (int i = 0; i < len; ++i){
            int id = s[i] - 'a';
            if (next[nod][id] == -1){
                next[nod][id] = newnode();
            }
            nod = next[nod][id];
        }
        flag[nod] = ii;
    }

    void bfs(){
        queue<int>q;

        fail[root] = root;

        for (int i = 0; i < 26; ++i){
            if (next[root][i] == -1){
                next[root][i] = root;
            }
            else {
                fail[next[root][i] ] = root;
                q.push(next[root][i]);
            }

        }


        while(!q.empty()){
            int u = q.front(); q.pop();
            flag[u] |= flag[fail[u] ];
            for (int i = 0; i < 26; ++i){
                if (next[u][i] == -1){
                    next[u][i] = next[fail[u] ][i];
                }
                else {
                    fail[next[u][i] ] = next[fail[u] ][i];
                    q.push(next[u][i]);
                }
            }
        }
    }

    vector<string>v;
    long long dfs(int i, int j, int k,int n,int m){
        long long& ans = dp[i][j][k];
        if (ans != -1) return ans;
        if (i == n){
            if (k == (1<<m) - 1){
                return ans = 1;
            }
            return ans = 0;
        }
        ans = 0;
        for (int l = 0; l < 26; ++l){
            int nx = next[j][l];
            char ch = 'a' + l;
            ans += dfs(i+1, nx, k | flag[nx],  n, m);
        }
        return ans;
    }

    void find(int i,int j,int k, string p, int n, int m){
        if (i >= n){
            if (k == (1<<m) - 1){
                v.push_back(p);
            }
            return;
        }
        if (dp[i][j][k] <= 0) return;

        for (int l = 0; l < 26; ++l){
            int nx = next[j][l];
            char ch = l + 'a';
            find(i+1, nx, k | flag[nx], p + ch, n, m);
        }

    }
    void solve(int n, int m){
        v.clear();
        memset(dp,-1,sizeof dp);
        long long ans = dfs(0,0,0,n, m);
        printf("%lld suspects\n", ans);
        if (ans <= 42){
            find(0,0,0,"", n, m);
            for (int i = 0; i < v.size(); ++i){
                printf("%s\n", v[i].c_str());
            }
        }
    }
}ac;
/**
4 1
hee

**/

int n, m;
char s[11][maxn];
int state[11];
int main(){
    int ks = 0;
    while(~scanf("%d %d",&n, &m)){
        if (n == 0 && m == 0) break;
        ac.init();
        for (int i = 0; i < m; ++i){
            scanf("%s", s[i]);
            state[i] = 0;
        }
        for (int i = 0; i < m; ++i){
            for (int j = 0; j < m; ++j){
                if (!strcmp(s[i], s[j])){
                    state[i] |= (1<<j);
                }
            }
        }
        for (int i = 0; i < m; ++i){
            ac.insert(s[i], state[i]);
        }
        ac.bfs();
        printf("Case %d: ",++ks);
        ac.solve(n, m);
    }
    return 0;
}