HDU 2825 Wireless Password (AC自动机 + 状压dp)

101 篇文章 1 订阅
15 篇文章 0 订阅

题意:

给你m(m <= 10)个串,要求你构造一个长度为n 的串, 使得至少包含m 个串中的k 个。求方案数

思路:

很像那种 构造一个串  不包含m 个串的方案数那种 , 但这种是包含。 

这个题串很小, 最多10个, 因此可以考虑状压dp。

那么很好想到了。

令dp[i][j][k] 表示目前构造串的第i 位, 在自动机 j 结点,  m 个串中选择的状态k 的方案数。


很好转移, 大家自己想想把。

不过写完之后TLE了。= =

然后就加了一堆小小优化。

比如说 取模改成加减法啊, 比如说 当前一个状态的dp 为0  就continue啊等等。(然后就608ms 过了。。 当然也写错了几个小地方,不知道是不是TLE 的错误 。。)

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

const int mod = 20090717;
void add(int& ans, int x){
    ans += x;
    if (ans >= mod) ans -= mod;
}

const int maxn = 100 + 1;

int bitcount(int x){
    int ans = 0;
    while(x){
        if (x & 1) ++ans;
        x>>=1;
    }
    return ans;
}

struct Trie{
    int L, root;
    int next[maxn][26];
    int fail[maxn];
    int flag[maxn];
    int sum[maxn];

    int dp[26][maxn][1024];


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

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

    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();
            sum[u] |= (flag[u] | sum[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]);
                }
            }


        }

    }
    void insert(char* s, int id){
        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] = 1 << id;
    }

    void solve(int n, int K,int m){
        for (int i = 0; i <= n; ++i){
            for (int j = 0; j < L; ++j){
                for (int k = 0; k < (1<<m); ++k){
                    dp[i][j][k] = 0;
                }
            }
        }

        dp[0][0][0] = 1;

        for (int i = 1; i <= n; ++i){
            for (int k = 0; k < (1<<m); ++k){
                for (int j = 0; j < L; ++j){
                    if (!dp[i-1][j][k]) continue;
                    for (int l = 0; l < 26; ++l){
                        int nx = next[j][l];
                        add(dp[i][nx][k | sum[nx] ], dp[i-1][j][k]);
                    }
                }
            }
        }

        int ans = 0;
        for (int j = 0; j < L; ++j){
            for (int i = 0; i < (1<<m); ++i){
                if (bitcount(i) >= K){
                    add(ans, dp[n][j][i]);
                }
            }
        }
        printf("%d\n", ans);
    }


};

int n, m, k;

char s[100];
Trie ac;
int main(){
    while(~scanf("%d %d %d",&n, &m, &k) && (n || m || k)){
        ac.init();
        for (int i = 0; i < m; ++i){
            scanf("%s", s);
            ac.insert(s, i);
        }
        ac.bfs();
        ac.solve(n, k, m);
    }
    return 0;
}


Wireless Password

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6832    Accepted Submission(s): 2270


Problem Description
Liyuan lives in a old apartment. One day, he suddenly found that there was a wireless network in the building. Liyuan did not know the password of the network, but he got some important information from his neighbor. He knew the password consists only of lowercase letters 'a'-'z', and he knew the length of the password. Furthermore, he got a magic word set, and his neighbor told him that the password included at least k words of the magic word set (the k words in the password possibly overlapping).

For instance, say that you know that the password is 3 characters long, and the magic word set includes 'she' and 'he'. Then the possible password is only 'she'.

Liyuan wants to know whether the information is enough to reduce the number of possible passwords. To answer this, please help him write a program that determines the number of possible passwords.
 

Input
There will be several data sets. Each data set will begin with a line with three integers n m k. n is the length of the password (1<=n<=25), m is the number of the words in the magic word set(0<=m<=10), and the number k denotes that the password included at least k words of the magic set. This is followed by m lines, each containing a word of the magic set, each word consists of between 1 and 10 lowercase letters 'a'-'z'. End of input will be marked by a line with n=0 m=0 k=0, which should not be processed.
 

Output
For each test case, please output the number of possible passwords MOD 20090717.
 

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

Sample Output
  
  
2 1 14195065
 

Source
 

Recommend
gaojie   |   We have carefully selected several similar problems for you:   2819  2824  2817  2823  2822 
 

Statistic |  Submit |  Discuss |  Note

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值