zoj 3494 BCD Code

zoj 3494 BCD Code

这题做的真是太艰难,水平太菜……是我的第一道ac自动机+ 数位dp的题目

思路都很容易想,不过这题要注意的实在太多了,稍不注意就让你调试半天,我就是搞了一个下午


1) 串输入进来的时候是高位在数组下标0,所以要反串

2) 前导0不能忽略考虑

3) A串要减1

4)dp每次case都要初始化为-1,应为每次的ac自动机建立的状态都不一样

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
const int mod=1000000009;
const int NODE=2010;
typedef long long ll;
ll dp[210][NODE];
char digit[210];
char dig[30];
int chd[NODE][2],word[NODE],fail[NODE],sz;

void ins(char *s)
{
    int p=0,len=strlen(s);
    for(int i=0;i<len;i++)
    {
        int id=s[i]-'0';
        if(!chd[p][id]){
            memset(chd[sz],0,sizeof(chd[sz]));
            word[sz]=0;
            chd[p][id]=sz++;
        }
        p=chd[p][id];
    }
    word[p]=1;
}
int Que[NODE];
void ac()
{
    int *s=Que,*e=Que;
    for(int i=0;i<2;i++)
       if(chd[0][i]) fail[chd[0][i]]=0,*e++=chd[0][i];
    while(s!=e)
    {
        int p=*s++;
        for(int i=0;i<2;i++)
          if(chd[p][i])
          {
              *e++=chd[p][i];
              fail[chd[p][i]]=chd[fail[p]][i];
              word[chd[p][i]]|=word[fail[chd[p][i]]];
          }else  chd[p][i]=chd[fail[p]][i];
    }
}
void init(int &ns,int d)
{
   for(int i=3;i>=0;i--)
   {
       int id=(d&(1<<i))?1:0;
       ns=chd[ns][id];
       if(word[ns])  { ns=-1;break; }
   }
}
ll dfs(int pos,int state,bool doing,bool ok)
{
    if(pos<0) return 1;
    if(ok&&!doing&&dp[pos][state]!=-1) return dp[pos][state];
    int end=doing?(digit[pos]-'0'):9;
    ll ans=0;

    for(int i=0;i<=end;i++)
    {
        int ns=state;
        if(ok||i) init(ns,i);
        if(ns!=-1)
           ans+=dfs(pos-1,ns,doing&&i==end,ok||i);
        ans%=mod;
    }

    if(ok&&!doing)  dp[pos][state]=ans;
    return ans;
}
void sub(char *s)
{
    int len=strlen(s),i;
    for(i=len-1;i>=0&&s[i]=='0';i--) s[i]='9';
    s[i]--;
}
ll cal(char *s)
{
    int len=strlen(s);
    int tot=0;
    for(int i=len-1;i>=0;i--) digit[tot++]=s[i];
    return dfs(tot-1,0,1,0);
}
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(dp,-1,sizeof(dp));
        sz=1;
        memset(chd[0],0,sizeof(chd[0]));
        for(int i=0;i<n;i++)
        {
            scanf("%s",dig);
            ins(dig);
        }
       ac();

       char tmp[210];
       scanf("%s",tmp);
       sub(tmp);
       ll ans1=cal(tmp);

        scanf("%s",tmp);
        ll ans2=cal(tmp);

         printf("%lld\n",((ans2-ans1)%mod+mod)%mod);
    }
    return 0;
}




你可能感兴趣的:(zoj 3494 BCD Code)