ZeroJudge-b179 空罐 Cans 可爱的AC自动机DP..

http://zerojudge.tw/ShowProblem?problemid=b179

     咩哈哈哈哈..这个只有29个人出的题俺整出来了...网上似乎还木有解题报告的说...我就来做第一个吧...

     这种关于包含病毒就要不得的问题很明显的要用到AC自动机...不管三七二十一..先根据所有病毒串做好AC自动机以及Trie图...

     本题的状态可以用dp[t][k][p]来表示.t代表当前"天"..k代表当前串长度...p代表当前串所能再AC自动机理匹配的最长后缀的末尾点...那么由于每一天只与前一天的状态有关..t可以用0,1的滚动数组来标记...k根据题目的说明最大100..p根据输入数据分析最大15*100=1500..so..dp[2][100][1500]可以记录题目所给的任何情况了...从时间上看,天数最多为300天..那么最多需要的操作数为300*100*1500=45000000..由于高中的OI比赛采用单点Judge...表示时间上也能接受...

     观察本题转移..当前有一个串..除了加'a' 'b' 'c' 'd'利用Trie图进行转移这种很基本的操作..还有一个就是去掉自身的第一个字符..而本题的关键就是这个..而这个操作可以分为3种情况:

     1.当前的长度为1..那么分裂完后显然就要丢资源回收站了..

     2.当前长度大于当前p所在Trie树中的深度,那么其去掉首字母后还会停留在Trie树中的p点..

     3.当前长度小于当前p所在Trie树中的深度,那么其去掉首字母后就需要转移了..沿着AC自动机的Fail指针去更新point[p].fail既是...    


Program:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#define oo 2000000000
#define ll long long
using namespace std; 
struct node1
{
       int son[4],fail,len;
       bool w;
}point[1510];
char s[105],str[105];
int p,n,dp[2][103][1510];
queue<int> myqueue;
int main()
{   
       freopen("input.txt","r",stdin);
       freopen("output.txt","w",stdout);
       int len,i,j,h,k,t,num,ans1,ans2;
       memset(point,0,sizeof(point));
       scanf("%s%d%d",str,&p,&n);
       num=0;
       while (n--)
       {
             scanf("%s",s);
             len=strlen(s);
             h=0;
             for (i=0;i<len;i++)
             {
                   if (!point[h].son[s[i]-'a'])
                   { 
                          point[h].son[s[i]-'a']=++num;
                          point[num].len=point[h].len+1;
                   }
                   h=point[h].son[s[i]-'a'];
                   if (point[h].w) break;
             }
             point[h].w=true;
       }
       while (!myqueue.empty()) myqueue.pop();
       for (i=0;i<4;i++) 
             if (point[0].son[i]) myqueue.push(point[0].son[i]);
       while (!myqueue.empty())
       {
             h=myqueue.front();
             myqueue.pop();
             if (point[point[h].fail].w) point[h].w=true;
             if (point[h].w) continue; 
             for (i=0;i<4;i++)
             {
                    k=point[h].fail;
                    while (k && !point[k].son[i]) k=point[k].fail;
                    point[point[h].son[i]].fail=point[k].son[i];
                    if (!point[h].son[i])
                       point[h].son[i]=point[k].son[i];
                    else
                       myqueue.push(point[h].son[i]);
             }
       }
       h=0;
       len=strlen(str);
       for (i=0;i<len;i++)
       {
             while (h && !point[h].son[str[i]-'a']) h=point[h].fail;
             h=point[h].son[str[i]-'a'];
             if (point[h].w) break;
       }
       k=0;
       memset(dp[k],0,sizeof(dp[k]));
       ans1=ans2=0;
       dp[k][len][h]=1;
       if (point[h].w) ans2=1;
       else 
       while (p--)
       { 
             k=1-k;
             memset(dp[k],0,sizeof(dp[k]));
             for (i=0;i<=num;i++)
               if (!point[i].w)
                 for (j=1;j<=100;j++)
                   if (dp[1-k][j][i])
                   {  
                         if (j==1) ans1=(ans1+dp[1-k][j][i])%10007;
                         else 
                         if (point[i].len>j-1)
                         {  
                                t=point[i].fail;
                                dp[k][j-1][t]+=dp[1-k][j][i]; 
                                dp[k][j-1][t]%=10007;  
                         }else
                         {
                                dp[k][j-1][i]+=dp[1-k][j][i];
                                dp[k][j-1][i]%=10007;
                         }
                         for (h=0;h<4;h++)
                         {
                                dp[k][j+1][point[i].son[h]]+=dp[1-k][j][i]; 
                                dp[k][j+1][point[i].son[h]]%=10007;
                                if (point[point[i].son[h]].w) 
                                    ans2=(ans2+dp[1-k][j][i])%10007;
                         }
                   }   
       }
       printf("%d %d\n",ans1,ans2);
       return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值