HDU2825 Wireless Password AC自动机+状压DP

来源:互联网 发布:公司网络管理需求分析 编辑:程序博客网 时间:2024/05/14 23:32

题意:
给出密码的长度n,可能含有密码字串的个数m和密码至少含有密码字串的个数k,求有多少种情况。
分析:
因为这个题不是问的密码字串必须全部包含,所以不能矩阵加速= =
果然n的大小变得很小只有25
可以用状压DP来做,具体是每个AC自动机内的节点都编个号,然后getfail的时候像以前矩阵加速getfail一样,假设当前节点的编号是2^k,当前节点的fail指向的点的编号是2^j,那么就把当前节点的编号更新成2^k+2^j(以前矩阵加速是fail节点不能选当前节点也不能选)。
然后dp[i][j][k]表示走到密码的第i位,在AC自动机上匹配到第j个点,状态为k的方案数。
dp[i][j][k]就可以推向dp[i+1][jj][k|cnt[jj]](cnt数组存放节点编号)。
竟然1A了,爽啊。

#include<iostream>#include<cstdio>#include<cstring>#include<queue>using namespace std;int a,b,c,mod=20090717;char s[30];struct ACautomata{    int next[255][26],fail[255],cnt[255],dp[26][255][1<<10],bitcnt[1<<10],num,root;    int newnode()    {        memset(next[num],0,sizeof next[num]);        cnt[num]=0;        return num++;    }    void init()    {        num=0;        root=newnode();        bitcnt[0]=0;        for(int i=1;i<(1<<10);++i)            bitcnt[i]=bitcnt[i>>1]+(i&1);    }    void insert(char *s,int l)    {        int len=strlen(s),cur=root;        for(int i=0;i<len;++i)        {            int &tmp=next[cur][s[i]-'a'];            if(!tmp)tmp=newnode();            cur=tmp;        }        cnt[cur]=l;    }    void getfail()    {        queue<int>q;        fail[root]=root;        for(int i=0;i<26;++i)        {            int u=next[root][i];            if(u)            {                fail[u]=0;                q.push(u);            }            else next[root][i]=0;        }        while(!q.empty())        {            int cur=q.front();            q.pop();            for(int i=0;i<26;++i)            {                int u=next[cur][i];                if(u)                {                    fail[u]=next[fail[cur]][i];                    cnt[u]|=cnt[fail[u]];                    q.push(u);                }                else next[cur][i]=next[fail[cur]][i];            }        }    }    int work(int n,int m,int k)    {        for(int i=0;i<=n;++i)            for(int j=0;j<num;++j)                for(int p=0;p<(1<<m);++p)                    dp[i][j][p]=0;        dp[0][0][0]=1;        for(int i=0;i<n;++i)            for(int j=0;j<num;++j)                for(int p=0;p<(1<<m);++p)                {                    if(dp[i][j][p]==0)continue;                    for(int l=0;l<26;++l)                    {                        int u=next[j][l];                        (dp[i+1][u][p|cnt[u]]+=dp[i][j][p])%=mod;                    }                }        int res=0;        for(int i=0;i<num;++i)            for(int j=0;j<(1<<m);++j)                if(bitcnt[j]>=k)(res+=dp[n][i][j])%=mod;        return res;    }}ac;int main(){    while(~scanf("%d%d%d",&a,&b,&c)&&(a+b+c))    {        ac.init();        for(int i=0;i<b;++i)            scanf("%s",s),ac.insert(s,1<<i);        ac.getfail();        printf("%d\n",ac.work(a,b,c));    }}
0 0