hdu2825 Wireless Password [AC自动机+状压dp]

Problem Description

给你一些字符串,你需要求出长度为n的字符串中,包含至少k个给定串的字符串有多少个。

Input

多组数据,第一行三个整数 n 表示所求串的长度(1<=n<=25), m 表示给定串的个数 (0<=m<=10) , 以及 k <script id="MathJax-Element-181" type="math/tex">k</script>表示至少包含k个给定串. 然后m行描述给定串, 每个串长度不超过10,由小写字母组成.
0 0 0表示输入结束。

Output

每组数据输出结果模20090717.

Sample Input

10 2 2
hello 
world 
4 1 1
icpc 
10 0 0
0 0 0

Sample Output

2
1
14195065

Solution

建立AC自动机然后状压。。
唉AC自动机玩的不6啊。。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>

using namespace std;

const int MOD = 20090717;

int n, m, k, tot, root;
int nex[105][26], fail[105], ed[105];

void insert(char str[], int id) {
    int len = strlen(str);
    int pos = root;
    for (int i = 0; i < len; i++) {
        int t = str[i] - 'a';
        if (!nex[pos][t])
            nex[pos][t] = ++tot;
        pos = nex[pos][t];
    }
    ed[pos] |= (1 << id);
}

void build() {
    queue<int> q;
    q.push(0);
    while (!q.empty()) {
        int p = q.front();
        q.pop();
        ed[p] |= ed[fail[p]];
        int t;
        for (int i = 0; i < 26; i++) {
            if (!(t = nex[p][i]))
                nex[p][i] = nex[fail[p]][i];
            else {
                fail[t] = p ? nex[fail[p]][i] : 0;
                q.push(t);
            }
        }
    }
}

int num[5030];
int dp[30][105][1 << 10];
int main() {
    for (int i = 0; i < 1 << 10; i++)
        for (int j = 0; j < 10; j++)
            num[i] += ((i >> j) & 1);
    while (~scanf("%d %d %d", &n, & m, &k)) {
        if (!n && !m && !k) break;
        char str[25];
        tot = root = 0;
        memset(ed, 0, sizeof(ed));
        memset(nex, 0, sizeof(nex));
        for (int i = 0; i < m; i++) {
            scanf("%s", str);
            insert(str, i);
        }
        build();
        memset(dp, 0, sizeof(dp));
        dp[0][0][0] = 1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= tot; j++) {
                for (int p = 0; p < 1 << m; p++) {
                    if (!dp[i][j][p]) continue;
                    for (int id = 0; id < 26; id++) {
                        int nj = (p | ed[nex[j][id]]);
                        dp[i + 1][nex[j][id]][nj] += dp[i][j][p];
                        dp[i + 1][nex[j][id]][nj] %= MOD;
                    }
                }
            }
        }
        int ans = 0;
        for (int p = 0; p < 1 << m; p++) {
            if (num[p] < k) continue;
            for (int i = 0; i <= tot; i++) {
                ans += dp[n][i][p];
                ans %= MOD;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值