HDOJ 2825 Wireless Password (AC自动机+DP)

题意:给m个字符串,求长为n且至少包含k个上述字符串的字符串有多少个。

数据范围:(1<=n<=25),(0<=m<=10)

分析:用dp[i][cur][s]表示走i步后,到达结点cur,包含的字符串压缩为状态s(第x位为1表示包含第x个字符串)的字符串有多少个。

TLE:当dp[i-1][pre][s]=0时,直接continue,不用进入下一层循环,经此优化后就AC了。

View Code
#include <stdio.h>

#include <string.h>

#include <queue>

using namespace std;

#define NODE 101

#define MOD 20090717



int n,m,cnt;

int next[NODE][26],fail[NODE],flag[NODE],node;

int dp[26][NODE][1<<10];



int newnode()

{

    fail[node]=flag[node]=0;

    memset(next[node],0,sizeof(next[0]));

    return node++;

}

void insert(char *s,int id)

{

    int i,k,cur;

    for(i=cur=0;s[i];i++)

    {

        k=s[i]-'a';

        if(!next[cur][k])   next[cur][k]=newnode();

        cur=next[cur][k];

    }

    flag[cur] |=(1<<id);

}

void makenext()

{

    int u,v;

    queue<int>q;



    q.push(0);

    while(!q.empty())

    {

        u=q.front(),q.pop();

        for(int k=0;k<26;k++)

        {

            v=next[u][k];

            if(v)   q.push(v);

            else    next[u][k]=next[fail[u]][k];

            if(u&&v)

            {

                fail[v]=next[fail[u]][k];

                flag[v] |=flag[fail[v]];

            }

        }

    }

}

int cal(int s)

{

    int ret=0;

    while(s)

    {

        ret+=s&1;

        s>>=1;

    }

    return ret;

}

void DP()

{

    for(int i=0;i<=n;i++)

    {

        for(int j=0;j<node;j++)

            memset(dp[i][j],0,sizeof(dp[0][0][0])*(1<<m));

    }

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



    for(int i=1;i<=n;i++)

    {

        for(int pre=0;pre<node;pre++)

        {

            for(int s=0;s<(1<<m);s++)

            {

                if(dp[i-1][pre][s]==0)  continue;

                for(int k=0;k<26;k++)

                {

                    int cur=next[pre][k];

                    int ns=s | flag[cur];

                    dp[i][cur][ns]+=dp[i-1][pre][s];

                    dp[i][cur][ns]%=MOD;

                }

            }

        }

    }

    int ans=0;

    for(int cur=0;cur<node;cur++)

    {

        for(int s=0;s<(1<<m);s++)

        {

            if(cal(s)>=cnt)

            {

                ans+=dp[n][cur][s];

                ans%=MOD;

            }

        }

    }

    printf("%d\n",ans);

}

int main()

{

    char s[11];

    while(scanf("%d%d%d",&n,&m,&cnt))

    {

        if(!n&&!m&&!cnt)    break;

        node=0,newnode();

        for(int i=0;i<m;i++)

        {

            scanf("%s",s);

            insert(s,i);

        }

        makenext();

        DP();

    }

    return 0;

}

你可能感兴趣的:(password)