高中生程式解题系统 b179: 空罐 Cans(自动机DP)

题目链接:http://zerojudge.tw/ShowProblem?problemid=b179

题意:每一天一个基因串,例如abc,分裂成4个abca,abcb,abcc,abcd,并且串自身长度减小1,变成bc。当长度为0时被回收站回收。给出m个病毒串,当一个串含有病毒串时马上将被送到医院。n天后回收站和医院的串各为多少?

思路:f[i][j][k]表示第i天长度为j的串在自动机的k节点上的串的个数。自动机上每个节点记录一个len。两种转移:

(1)向下增长1,比较简单,对于k的子节点h,f[i+1][j+1][h]+=f[i][j][k];

(2)自身减去1:若j=1则直接放回回收站;若当前节点k的len值大于j-1,那么找到k的fail节点h,f[i+1][j-1][h]+=f[i][j][k];否则f[i+1][j-1][k]+=f[i][j][k]。




struct node
{
    int next[4],fail,len,flag;

    void init()
    {
        clr(next,0);
        fail=0;
        flag=len=0;
    }
};

node a[N];
int e,n,m;


void insert(char s[])
{
    int i,k,p=0;
    for(i=0;s[i];i++)
    {
        k=s[i]-'a';
        if(a[p].next[k]==0)
        {
            a[e].init();
            a[e].len=a[p].len+1;
            a[p].next[k]=e++;
        }
        p=a[p].next[k];
    }
    a[p].flag=1;
}


queue<int> Q;

void build()
{
    int i,j,k,p,q;
    FOR0(i,4) if(a[0].next[i]) Q.push(a[0].next[i]);
    while(!Q.empty())
    {
        k=Q.front();
        Q.pop();
        for(i=0;i<4;i++)
        {
            if(a[k].next[i])
            {
                p=a[k].next[i];
                q=a[k].fail;
                Q.push(p);
                a[p].fail=a[q].next[i];
                a[p].flag|=a[a[p].fail].flag;
            }
            else
            {
                q=a[k].fail;
                a[k].next[i]=a[q].next[i];
            }
        }
    }
}

char s[50005],str[10005];
int f[2][105][1505];


void up(int &x,int y)
{
    x=(x+y)%10007;
}


void DP()
{
    int p=0,i,j,k,t,h,temp,len=strlen(str);
    for(i=0;str[i];i++)
    {
        k=str[i]-'a';
        while(p&&!a[p].next[k]) p=a[p].fail;
        p=a[p].next[k];
        if(a[p].flag) break;
    }
    if(a[p].flag)
    {
        puts("0 1");
        return;
    }
    int ans1=0,ans2=0,pre=0,cur=1;
    k=0;
    clr(f[0],0);
    f[0][len][p]=1;

    while(n--)
    {
        clr(f[cur],0);
        for(i=0;i<e;i++) if(!a[i].flag) for(j=1;j<=100;j++) if(f[pre][j][i])
        {
            temp=f[pre][j][i];
            if(j==1) up(ans1,temp);
            else if(a[i].len>j-1)
            {
                t=a[i].fail;
                up(f[cur][j-1][t],temp);
            }
            else up(f[cur][j-1][i],temp);
            for(h=0;h<4;h++)
            {
                t=a[i].next[h];
                up(f[cur][j+1][t],temp);
                if(a[t].flag) up(ans2,temp);
            }
        }
        pre^=1;
        cur^=1;
    }
    printf("%d %d\n",ans1,ans2);
}


int main()
{
    while(scanf("%s%d%d",str,&n,&m)!=-1)
    {
        a[0].init();e=1;
        int i;
        FOR0(i,m) RD(s),insert(s);
        build();
        DP();
    }
    return 0;
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值