HDOJ-2825 AC自动机DP+位运算..

     题目是要求最少出现k种模式串...开始我看成k个了..囧..k种的话可以用2^k-1的整数可以描述出模式串出现的任意情况...也就是将每个模式串出现否看成二进制的1,0...题目中模式串最多10个..所以模式串存在状态最多1023种...

     先用AC自动机构造Trie图...这个已经很模板很模板了..值得注意的是在构造时给每个点我称为data的值..代表从0点出发到这个点的串的后缀子串能是哪些模式串..为了保证完备性..应该将其fail的情况继承下来..这个过程举例子..假设当前点包含1,3号串..而其fail包含1,2号串...那么当前点就应该包含1,2,3号串..用二进制的角度看...当前串point[h].data=101..其fail: point[point[h].fail].data=011...而当前串应该变成point[h].data=111...这实际上就是二进制的或运算了..

     dp时每个状态可以用三个值确定...1.当前长度,2.当前点标号.3.当前点标号下包含哪些串,也就是个十进制表示的二进制数..Trie图中每个点是状态点..有向边是转移方向..转移时同样也是通过二进制的或运算判断是当前状态转移到下个状态包含哪些串..

     由于每个长度p的状态只于p-1长度的状态有关...所以可以用滚动数组来节约空间...

     结果的话..找出所有包含模式串>k情况的..将这些dp值都加起来...O了~


Program:

#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
struct node1
{
       int son[26],fail,data; 
       char w;
}point[102];
int n,m,k,dp[2][102][1025];
char s[12];
queue<int> myqueue;
bool used[105];
int ok(int x)
{
       int a=0;
       while (x)
       {
             a+=x%2;
             x/=2;
       }
       return a;
}
int main()
{
       int i,j,h,len,p,x,num,ans,t;
       while (~scanf("%d%d%d\n",&n,&m,&k))
       {
             if (!n && !m && !k) break;
             memset(point,0,sizeof(point));
             num=0; 
             for (i=0;i<m;i++)
             {
                   scanf("%s",s);
                   len=strlen(s);
                   h=0;
                   for (j=0;j<len;j++)
                   {
                          if (!point[h].son[s[j]-'a']) 
                          {
                               point[h].son[s[j]-'a']=++num; 
                               point[num].w=s[j];
                          }
                          h=point[h].son[s[j]-'a'];
                   }
                   point[h].data=1<<i; 
             }
             while (!myqueue.empty()) myqueue.pop();
             for (i=0;i<26;i++)
                if (point[0].son[i]) myqueue.push(point[0].son[i]);
             while (!myqueue.empty())
             {
                   h=myqueue.front();
                   myqueue.pop(); 
                   point[h].data|=point[point[h].fail].data;
                   for (i=0;i<26;i++)
                   { 
                          p=point[h].fail;
                          while (p && !point[p].son[i]) p=point[p].fail;
                          point[point[h].son[i]].fail=point[p].son[i];
                          if (!point[h].son[i]) 
                               point[h].son[i]=point[p].son[i];
                          else
                               myqueue.push(point[h].son[i]);                 
                   }
             }
             memset(dp[0],0,sizeof(dp[0]));
             dp[0][0][0]=1;
             p=0;
             t=(1<<m)-1;
             while (n--)
             { 
                   p=1-p;
                   memset(dp[p],0,sizeof(dp[p]));
                   memset(used,false,sizeof(used));
                   used[0]=true;
                   myqueue.push(0);
                   while (!myqueue.empty())
                   {
                         h=myqueue.front();
                         myqueue.pop(); 
                         for (j=0;j<=t;j++)
                           if (dp[1-p][h][j])
                             for (i=0;i<26;i++)
                             {
                                    if (!used[point[h].son[i]])
                                    {
                                           myqueue.push(point[h].son[i]);
                                           used[point[h].son[i]]=true;
                                    }
                                    x=j|point[point[h].son[i]].data; 
                                    dp[p][point[h].son[i]][x]+=dp[1-p][h][j];
                                    dp[p][point[h].son[i]][x]%=20090717;
                             }
                   } 
             }
             ans=0;
             for (j=0;j<=t;j++)
               if (ok(j)>=k)
                 for (i=0;i<=num;i++) ans=(ans+dp[p][i][j])%20090717;
             printf("%d\n",ans);
       }
       return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值