[ZOJ3494]BCD Code

  AC自动机+数位DP。

  大致题意:

    BCD码就是把一个数十进制下的每一位分别用4位的二进制表示。

    给你一坨01串,问你在一个区间内,有多少个数的BCD码不包含任何一个字符串。

 

  因为涉及到多个串的匹配问题...所以要在AC自动机上DP

  f[i][j]表示在自动机上的节点i,再往后走j步 的合法方案数。(合法就是说,经过的路径上不包含任何一个给定字符串)

  建完AC自动机后,把非法的节点都删掉,再求出CH[i][j]表示从i节点出发,经过数字j后到达的节点(0<=j<=9),这样好转移= =。

 

  一开始想写递归版本的。。前导0什么的完全无力TAT

  后来干脆用记忆化搜索求f数组,然后统计答案的时候用正常姿势。。这样好写多了>_<

  结果交完直接#1了。。我是没看出来自己的写法哪里常数优越= =

  其实这题只比普通模版题多了个AC自动机而已。。。其他甚至还简单点

  就算是递归版的,只要好好想一下前导零的问题就行了吧...网上递归版的标程也不长

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int modd=1000000009;
 6 int fail[2003],ch[2003][2];
 7 int CH[2003][10];
 8 int f[2003][202],u[2003][202];//f[i][j]在自动机上的i节点时,再往后走j步的合法方案数 
 9 int i,j,k,n,m,tot,T,ans,len;
10 bool gg[2003];
11 
12 char s[233];int dl[2333];
13 
14 inline void insert(int len){
15     int i,now=0;
16     for(i=0;i<len;i++)
17         s[i]-='0',now=ch[now][s[i]]?ch[now][s[i]]:(ch[now][s[i]]=++tot);
18     gg[now]=1;
19 }
20 inline void build(){
21     int l=0,r=1,i,now,tmp;
22     while(l<r){
23         now=dl[++l];
24         for(i=0;i<=1;i++)if(ch[now][i]){
25             for(tmp=fail[now];tmp&&!ch[tmp][i];tmp=fail[tmp]);
26             fail[ch[now][i]]=(now==0)?0:ch[tmp][i];
27             
28             gg[ch[now][i]]|=gg[fail[ch[now][i]]]|gg[now];
29             dl[++r]=ch[now][i];
30         }else{
31             for(tmp=fail[now];tmp&&!ch[tmp][i];tmp=fail[tmp]);
32             ch[now][i]=ch[tmp][i];
33         }
34     }
35     for(i=0;i<=tot;i++)for(j=0;j<=1;j++)if(gg[ch[i][j]]||gg[i])ch[i][j]=-233;
36     for(i=0;i<=tot;i++)if(!gg[i])
37         for(j=0;j<=9;j++){
38             for(now=i,k=8;k&&now!=-233;k>>=1)
39                 now=ch[now][(k&j)!=0];
40             CH[i][j]=now;//printf("   %d->%d %d\n",i,j,CH[i][j]);
41         }
42     for(i=0;i<=tot;i++)if(!gg[i])f[i][0]=1,u[i][0]=T;
43 }
44 inline int dfs(int x,int step){
45     if(u[x][step]==T)return f[x][step];
46     int ans=0;
47     for(int i=0;i<=9;i++)if(CH[x][i]!=-233)
48         ans+=dfs(CH[x][i],step-1),ans-=ans>=modd?modd:0;
49     u[x][step]=T,f[x][step]=ans;//printf("   f: %d %d  %d\n",x,step,f[x][step]);
50     return ans;
51 }
52 inline int get(int len){//求区间[1,len)内合法方案数 
53     register int i,j,ans=0,now;
54     for(i=0;i<len;i++)s[i]-=48;
55     for(i=1;i<len;i++)for(j=1;j<=9;j++)if(CH[0][j]!=-233)
56         ans+=dfs(CH[0][j],i-1),ans-=ans>=modd?modd:0;
57     for(i=1;i<s[0];i++)if(CH[0][i]!=-233)ans+=dfs(CH[0][i],len-1),ans-=ans>=modd?modd:0;
58     
59     now=CH[0][s[0]];
60     for(i=1;i<len&&now!=-233;i++){
61         for(j=0;j<s[i];j++)if(CH[now][j]!=-233)ans+=dfs(CH[now][j],len-i-1),ans-=ans>=modd?modd:0;
62         now=CH[now][s[i]];
63     }
64     return ans;
65 }
66 inline void add(){
67     int i,j;
68     for(i=len-1;i>=0;i--)if(s[i]!='9')break;
69     if(i>=0)
70         for(s[i]++,j=i+1;j<len;j++)s[j]='0';
71     else{
72         for(s[0]='1',i=1;i<=len;i++)s[i]='0';
73         len++;
74     }
75 }
76 int main(){
77     for(scanf("%d",&T);T;T--){
78         scanf("%d",&n);memset(gg,0,tot+1);memset(ch,0,(tot+1)<<3);tot=0;
79         for(i=1;i<=n;i++)
80             scanf("%s",s),insert(strlen(s));
81         build();
82         scanf("%s",s);len=strlen(s);ans=-get(len);
83         scanf("%s",s);len=strlen(s);add();ans+=get(len);
84         if(ans<0)ans+=modd;
85         printf("%d\n",ans);
86     }
87     return 0;
88 }
View Code

 

posted @ 2016-02-26 21:09  czllgzmzl  阅读(477)  评论(0编辑  收藏  举报