您的位置:首页 > 其它

[ZOJ3494]BCD Code

2016-02-26 21:09 232 查看
  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数组,然后统计答案的时候用正常姿势。。这样好写多了>_<

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int modd=1000000009;
int fail[2003],ch[2003][2];
int CH[2003][10];
int f[2003][202],u[2003][202];//f[i][j]在自动机上的i节点时,再往后走j步的合法方案数
int i,j,k,n,m,tot,T,ans,len;
bool gg[2003];

char s[233];int dl[2333];

inline void insert(int len){
int i,now=0;
for(i=0;i<len;i++)
s[i]-='0',now=ch[now][s[i]]?ch[now][s[i]]:(ch[now][s[i]]=++tot);
gg[now]=1;
}
inline void build(){
int l=0,r=1,i,now,tmp;
while(l<r){
now=dl[++l];
for(i=0;i<=1;i++)if(ch[now][i]){
for(tmp=fail[now];tmp&&!ch[tmp][i];tmp=fail[tmp]);
fail[ch[now][i]]=(now==0)?0:ch[tmp][i];

gg[ch[now][i]]|=gg[fail[ch[now][i]]]|gg[now];
dl[++r]=ch[now][i];
}else{
for(tmp=fail[now];tmp&&!ch[tmp][i];tmp=fail[tmp]);
ch[now][i]=ch[tmp][i];
}
}
for(i=0;i<=tot;i++)for(j=0;j<=1;j++)if(gg[ch[i][j]]||gg[i])ch[i][j]=-233;
for(i=0;i<=tot;i++)if(!gg[i])
for(j=0;j<=9;j++){
for(now=i,k=8;k&&now!=-233;k>>=1)
now=ch[now][(k&j)!=0];
CH[i][j]=now;//printf("   %d->%d %d\n",i,j,CH[i][j]);
}
for(i=0;i<=tot;i++)if(!gg[i])f[i][0]=1,u[i][0]=T;
}
inline int dfs(int x,int step){
if(u[x][step]==T)return f[x][step];
int ans=0;
for(int i=0;i<=9;i++)if(CH[x][i]!=-233)
ans+=dfs(CH[x][i],step-1),ans-=ans>=modd?modd:0;
u[x][step]=T,f[x][step]=ans;//printf("   f: %d %d  %d\n",x,step,f[x][step]);
return ans;
}
inline int get(int len){//求区间[1,len)内合法方案数
register int i,j,ans=0,now;
for(i=0;i<len;i++)s[i]-=48;
for(i=1;i<len;i++)for(j=1;j<=9;j++)if(CH[0][j]!=-233)
ans+=dfs(CH[0][j],i-1),ans-=ans>=modd?modd:0;
for(i=1;i<s[0];i++)if(CH[0][i]!=-233)ans+=dfs(CH[0][i],len-1),ans-=ans>=modd?modd:0;

now=CH[0][s[0]];
for(i=1;i<len&&now!=-233;i++){
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;
now=CH[now][s[i]];
}
return ans;
}
inline void add(){
int i,j;
for(i=len-1;i>=0;i--)if(s[i]!='9')break;
if(i>=0)
for(s[i]++,j=i+1;j<len;j++)s[j]='0';
else{
for(s[0]='1',i=1;i<=len;i++)s[i]='0';
len++;
}
}
int main(){
for(scanf("%d",&T);T;T--){
scanf("%d",&n);memset(gg,0,tot+1);memset(ch,0,(tot+1)<<3);tot=0;
for(i=1;i<=n;i++)
scanf("%s",s),insert(strlen(s));
build();
scanf("%s",s);len=strlen(s);ans=-get(len);
scanf("%s",s);len=strlen(s);add();ans+=get(len);
if(ans<0)ans+=modd;
printf("%d\n",ans);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: