ZOJ 3494 (AC自动机+数位DP)

题意:给n个01字符串,求区间[x , y]中有多少个数写成BCD码后不包含以上01串。

分析:先用01字符串建立AC自动机(注意标记危险结点),然后DP。dp[i][s]表示扫描前i位后有多少个数会到达自动机的结点s.

注意:

1.前导0的问题:不能转化成二进制在数位dp(否则处理不了前导0),安十进制进行数位dp!

2.d[u][sub][zero]的sub是从n->0,不是从0->n(sub表示还剩几位)    Orz。。。

//#pragma comment(linker, "/STACK:102400000")
#include<cstdlib>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<vector>
#define tree int o,int l,int r
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define lo o<<1
#define ro o<<1|1
#define pb push_back
#define mp make_pair
#define ULL unsigned long long
#define LL long long
#define inf 0x7fffffff
#define eps 1e-7
#define N 2009
#define M 2
#define mod 1000000009
using namespace std;
int m,n,T,t,x,y,u;
int ch[N][M],v[N],sz;
int f[N],last[N],len;
char str[29],a[209],b[209],*s;
int d[N][809][2];
char tr[10][5]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001"};
void init()
{
    sz=1;
    memset(ch[0],0,sizeof(ch[0]));
    memset(v,0,sizeof(v));
    memset(last,0,sizeof(last));
    memset(d,-1,sizeof(d));
}
int idx(char c)
{
    return c-'0';
}
void insert(char str[],int val)
{
    int u=0;
    for(int i=0; str[i]; i++)
    {
        int c=idx(str[i]);
        if(!ch[u][c])
        {
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[u][c]=sz++;
        }
        u=ch[u][c];
    }
    v[u]=val;
}
void getac()
{
    f[0]=0;
    queue<int>q;
    for(int c=0; c<M; c++)
    {
        int u=ch[0][c];
        if(u)
        {
            f[u]=0;
            q.push(u);
            last[u]=v[u];//////////////////
        }
    }
    while(!q.empty())
    {
        int r=q.front();
        q.pop();
        for(int c=0; c<M; c++)
        {
            int u=ch[r][c];
            if(!u)
            {
                ch[r][c]=ch[f[r]][c];
            }
            else
            {
                q.push(u);
                int s=f[r];
                f[u]=ch[s][c];
                last[u]=(v[u]||last[f[u]]);/////////////////
            }
        }
    }
}
int ok(int u,int k)
{
    for(int i=3;i>=0;i--)
    {
        u=ch[u][((k>>i)&1)];
        if(last[u])
        {
            u=-1;break;
        }
    }
    return u;
}
int dp(int u,int sub,int up,int zero)//sub表示还剩下的位数,0表示是否前导0
{
    if(sub==0)return 1;
    if(d[u][sub][zero]!=-1&&(!up))
    return d[u][sub][zero];
    int ans=0;
    int end=(up?idx(s[n-sub]):9);
    int i=0;
    if(zero)//处理前导0
    {
        ans+=dp(u,sub-1,up&&(0==end),1);
        ans%=mod;
        i++;
    }
    for(;i<=end;i++)
    {
        int c=ok(u,i);
        if(c!=-1)
        {
            ans+=dp(c,sub-1,up&&(i==end),0);
            ans%=mod;
        }
    }
    if(!up)
    d[u][sub][zero]=ans;
    return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("ex.in","r",stdin);
#endif
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d",&n);
        for(int i=1; i<=n; ++i)
        {
            scanf("%s",str);
            insert(str,1);
        }
        getac();
        scanf("%s%s",a,b);
        n=strlen(a);
        for(int i=n-1;i>=0;i--)
        if(a[i]=='0')
            a[i]='9';
        else
        {
            a[i]--;break;//有前导0也没事
        }
        s=a;
        int ans=dp(0,n,1,1);//d[u][sub][zero]的sub是从n->0,不是从0->n(WA)
        s=b;
        n=strlen(b);
        int ans2=dp(0,n,1,1);
        ans=ans2-ans;
        if(ans<0)ans+=mod;
        ans%=mod;
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2013-10-19 10:15  baoff  阅读(245)  评论(0编辑  收藏  举报