hdu 2825 Wireless Password (ac自动机+状压dp)

来源:互联网 发布:115网盘会员充值淘宝 编辑:程序博客网 时间:2024/05/17 01:22

题意:

给出m个串,现在一个n个字母的字句里面必须包含至少p个m集合中的串。问有多少种方式。

题解:

kuangbin说ac自动机状态转移图什么,不是很懂。但是抛开这个,去思考也是可以做的。我们在ac自动机上dp肯定要设置这样的状态dp[i][j]走了i步,结尾是j节点,但是这样明显无法获得由几m集合中的串,分析发现这类似排列组合,要实现这样的计数问题,只能是状压dp,那么我们多设置一维dp[i][j][s]s表示获得m集合中串的状态。最后只要得到状态中1个数大于p的答案就好。


#include<iostream>#include<math.h>#include<stdio.h>#include<algorithm>#include<string.h>#include<vector>#include<map>#include<set>using namespace std;#define B(x) (1<<(x))typedef long long ll;const int oo=0x3f3f3f3f;const ll OO=1LL<<61;const ll MOD=20090717;const int maxn=1005;const int SIZE=110;char str[maxn];int dp[27][SIZE][B(10)+5];int num[B(10)+5];struct AC{    int next[SIZE][26],fail[SIZE],end[SIZE],Q[SIZE*26];    int root,cnt;    void Init()    {        cnt=0;        root=newNode();    }    int newNode()    {        for(int i=0;i<26;i++)            next[cnt][i]=-1;        end[cnt++]=0;        return cnt-1;    }    void Insert(char buff[],int id)    {        int now=root;        int len=strlen(buff);        for(int i=0,k;i<len;i++)        {            k=buff[i]-'a';            if(next[now][k]==-1)                next[now][k]=newNode();            now=next[now][k];        }        end[now]|=B(id);///这个单词是否存在    }    void build()    {        fail[root]=root;        int front,rear;        front=rear=0;        int now=root;        for(int i=0;i<26;i++)        {            if(next[now][i]==-1)                next[now][i]=root;            else            {                fail[next[now][i]]=root;                Q[rear++]=next[now][i];            }        }        while(front<rear)        {            now=Q[front++];            end[now]|=end[fail[now]];///重要            for(int i=0;i<26;i++)            {                if(next[now][i]==-1)                    next[now][i]=next[fail[now]][i];                else                {                    fail[next[now][i]]=next[fail[now]][i];                    Q[rear++]=next[now][i];                }            }        }    }    void add(int& a,int b)    {        a=(a+b)%MOD;    }    int DP(int n,int m,int p)    {        int full=B(m)-1;        //memset(dp,0,sizeof dp);        for(int i=0;i<=n;i++)            for(int j=0;j<cnt;j++)                for(int s=0;s<=full;s++)                    dp[i][j][s]=0;        dp[0][0][0]=1;        for(int i=0;i<n;i++)        {            for(int j=0;j<cnt;j++)            {                for(int s=0;s<=full;s++)                if(dp[i][j][s])                {                    for(int t=0;t<26;t++)                    {                        int k=next[j][t];                        int st=s|end[k];                        add(dp[i+1][k][st],dp[i][j][s]);                    }                }            }        }        int ans=0;        for(int s=0;s<=full;s++)            if(num[s]>=p)                for(int i=0;i<cnt;i++)                    add(ans,dp[n][i][s]);        return ans;    }};AC ac;void Init(){    int S=B(10);    for(int i=0;i<S;i++)    {        num[i]=0;        for(int j=0;j<10;j++)            if(i&B(j))                num[i]++;    }}int main(){    Init();    int n,m,p;    while(scanf("%d %d %d",&n,&m,&p)!=EOF)    {        if(n==0&&m==0&&p==0)break;        ac.Init();        for(int i=0;i<m;i++)        {            scanf("%s",str);            ac.Insert(str,i);        }        ac.build();        cout<<ac.DP(n,m,p)<<endl;    }    return 0;}/** 10 2 2helloworld4 1 1icpc10 0 00 0 0*/



0 0
原创粉丝点击