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

29 篇文章 1 订阅

题意:给出一些禁止串,问从A~B的数字用BCD码表示,并且不包含禁止串的数有多少。

思路:首先用禁止串建立AC自动机,然后数位DP……dp[i][j]表示在位置i,状态为j时的合法数字个数。另外,如果最高位是0,那么这是不能被算在内的,所以,要再用个值来表示前面是否全为0。

代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=2000+10;
const int maxl=200+10;
const int mod=1000000009;
int ch[maxn][2],next[maxn],flag[maxn],size;
int num[maxl],dp[maxl][maxn];
char str[maxn];
void Init()
{
    ch[0][0]=ch[0][1]=0;
    memset(next,0,sizeof(next));
    flag[0]=size=0;
    memset(dp,0xff,sizeof(dp));
}
void Insert(const char *s)
{
    int u=0,n=strlen(s);
    for(int i=0;i<n;++i)
    {
        int c=s[i]-'0';
        if(!ch[u][c])
        {
            ch[u][c]=++size;
            ch[size][0]=ch[size][1]=0;
            flag[size]=0;
        }
        u=ch[u][c];
    }
    flag[u]=1;
}
void build()
{
    queue<int>q;
    if(ch[0][0]) q.push(ch[0][0]);
    if(ch[0][1]) q.push(ch[0][1]);
    int r,u,v;
    while(!q.empty())
    {
        r=q.front();q.pop();
        for(int c=0;c<2;++c)
        {
            u=ch[r][c];
            if(!u) {ch[r][c]=ch[next[r]][c];continue;}
            q.push(u);
            v=next[r];
            while(v&&!ch[v][c]) v=next[v];
            next[u]=ch[v][c];
            flag[u]|=flag[next[u]];
        }
    }
}
int f(int pos,int st,bool zero,bool limit)
{
    if(pos==-1) return 1;
    if(!limit&&dp[pos][st]!=-1)
        return dp[pos][st];
    int ans=0,u,i=0,last;
    if(pos>0&&zero)
    {
        ans=(ans+f(pos-1,0,true,limit&&num[pos]==0))%mod;
        i=1;
    }
    last=limit?num[pos]:9;
    for(;i<=last;++i)
    {
        u=st;
        for(int j=3;j>=0;--j)
        {
            u=ch[u][(i&(1<<j))!=0];
            if(flag[u]) {u=-1;break;}
        }
        if(u!=-1)
            ans=(ans+f(pos-1,u,false,limit&&i==last))%mod;
    }
    if(!limit&&!zero) dp[pos][st]=ans;
    return ans;
}
int cal(bool d)
{
    memset(num,0,sizeof(num));
    int len=strlen(str);
    for(int i=0;i<len;++i)
        num[i]=str[len-i-1]-'0';
    if(d)
    {
        num[0]--;
        int i=0;
        while(num[i]<0)
        {
            num[i++]=9;
            num[i]--;
        }
        if(!num[len-1]&&len!=1) len--;
    }
    return f(len-1,0,true,true);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        Init();
        scanf("%d",&n);
        for(int i=0;i<n;++i)
        {
            scanf("%s",str);
            Insert(str);
        }
        build();
        int res=0;
        scanf("%s",str);
        res-=cal(true);
        scanf("%s",str);
        res+=cal(false);
        res=(res%mod+mod)%mod;
        printf("%d\n",res);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值