Wireless Password

题目大意:有个人想破解他邻居的密码,他邻居告诉了一些关于这个密码的信息,并且给他一个单词集合,他用这些信息判断一下最少有多少种密码。
1->, 所有的密码都是有小写字母组成。
2->,密码的长度是 n (1<= n <=25)。
3->,密码至少包含 k 种字符集里面的单词。
 
比如,给集合{"she", "he"},单词长度是3,最少包含两个单词的密码,很明显只能是“she”(题目表述的不清楚)。
 
分析:因为要统计记录到达每个点时候经过多少种不同的单词,所以需要用一种方法来保存这些信息,开一个数组来判重貌似是个很容易想到的办法,不过考虑时间复杂度的问题,建议还是不要这么干,因为字符集合的数量很少,只有10,所以我们可以使用状态压缩来保存这些信息,而且转移状态时候也很方便操作,不过有点需要注意,一定要把遍历子节点放在最内层循环,这样再遍历每一种状态的时候发先有0的情况可以continue一下,否则超时超的哇哇的.......
 
代码如下:
==========================================================================
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

const int MAXN = 107;
const int MaxSon = 26;
const int Mod = 20090717;
const int oo = 1e9+7;

struct Ac_Trie
{
    int next[MAXN][MaxSon];
    int Fail[MAXN], End[MAXN];
    int cnt, root;

    int newnode()
    {
        for(int i=0; i<MaxSon; i++)
            next[cnt][i] = -1;
        Fail[cnt] = End[cnt] = false;

        return cnt++;
    }
    void InIt()
    {
        cnt = 0;
        root = newnode();
    }

    void Insert(char s[], int t)
    {
        int now = root;

        for(int i=0; s[i]; i++)
        {
            int k = s[i]-'a';

            if(next[now][k] == -1)
                next[now][k] = newnode();
            now = next[now][k];
        }

        End[now] = 1<<t;
    }
    void GetFial()
    {
        queue<int>Q;
        int now = root;

        for(int i=0; i<MaxSon; i++)
        {
            if(next[now][i] == -1)
                next[now][i] = root;
            else
            {
                Fail[next[now][i]] = root;
                Q.push(next[now][i]);
            }
        }

        while(Q.size())
        {
            now = Q.front();
            Q.pop();

            for(int i=0; i<MaxSon; i++)
            {
                if(next[now][i] == -1)
                    next[now][i] = next[Fail[now]][i];
                else
                {
                    Fail[next[now][i]] = next[Fail[now]][i];
                    Q.push(next[now][i]);
                }
            }

            End[now] |= End[Fail[now]];
        }
    }
};
Ac_Trie ac;

int Find(int i)
{
    int k=0;

    while(i)
    {
        if(i % 2)
            k++;
        i /= 2;
    }

    return k;
}

int main()
{
    int N, M, K;

    int sum[1024] ={0};

    for(int i=0; i<1024; i++)
        sum[i] = Find(i);

    while(scanf("%d%d%d", &N, &M, &K), N+M+K)
    {
        char s[MAXN];
        ac.InIt();

        for(int i=0; i<M; i++)
        {
            scanf("%s", s);
            ac.Insert(s, i);
        }

        ac.GetFial();

        int dp[2][MAXN][1024] = {0}, op=0, Len = 1<<M;
        dp[1][0][0] = 1;

        while(N--)
        {
            memset(dp[op], 0, sizeof(dp[op]));

            for(int i=0; i<ac.cnt; i++)
            for(int k=0; k<Len; k++)
            {///把k放中间优化一下...否则超时
                if(dp[op^1][i][k] == 0)
                    continue;

                for(int j=0; j<MaxSon; j++)
                {
                    (dp[op][ac.next[i][j]][k|ac.End[i]] += dp[op^1][i][k])%=Mod;
                }
            }

            op ^= 1;
        }

        int ans = 0;

        for(int i=0; i<ac.cnt; i++)
        for(int j=0; j<Len; j++)
        {
            if(sum[j] >= K || sum[ac.End[i]|j] >= K)
            {
                ans += dp[op^1][i][j];
                ans %= Mod;
            }
        }

        printf("%d
", ans);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/liuxin13/p/4758136.html