ZOJ3494 BCD Code-AC自动机+数位DP

vjudge传送门

题意:

每一位数对应一种BCD编码:

Decimal:    0     1     2     3     4     5     6     7     8     9
BCD:     0000  0001  0010  0011  0100  0101  0110  0111  1000  1001r

如127对应的BCD编码是0001 0010 0111

先给出n个不合法的BCD编码串,求[A,B]中转化成BCD编码后不包含这n个编码串中的任意一个的数的个数

0<AB<10200 0 < A ≤ B < 10 200

n<=100,字符串长<=20

Solution:

一眼AC自动机+数位dp

f[i][j][0/1][0/1] f [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] 表示前i位,在AC自动机的j节点,是否到达上界,是否是前导零

记忆化搜索即可

此题用来练习代码能力不错
注意一定要dfs!!!

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int root=1;
const int mod=1e9+9;
int siz,tr[2010][2],tag[2010],ans;
int T,n;
char s[110],num[210];
int tot;
int fail[2010];
int q[2010],h,t;
int f[210][2010][2][2];
int le[10];
int a[10][4];
bool p=0;
bool vis[210][2010][2][2];
int size,head[2010];
struct eg{
    int to,next;
}e[2010];
void add(int x,int y)
{
    size++;
    e[size].to=y;
    e[size].next=head[x];
    head[x]=size;
}
void insert(char s[])
{
    int nw=root,len=strlen(s);
    for (int i=0;i<len;i++)
    {
        if (!tr[nw][s[i]-'0'])
        {
            tr[nw][s[i]-'0']=++siz;
            fail[siz]=tr[siz][0]=tr[siz][1]=tag[siz]=0;
        }
        nw=tr[nw][s[i]-'0'];
    }
    tag[nw]=1;
}
void get_fail()
{
    h=t=0;
    q[++t]=root;
    while (h<t)
    {
        int x=q[++h];
        for (int i=0;i<=1;i++)
            if (tr[x][i])
            {
                if (x==root) fail[tr[x][i]]=root,add(root,tr[x][i]);
                else fail[tr[x][i]]=tr[fail[x]][i],add(tr[fail[x]][i],tr[x][i]);
                q[++t]=tr[x][i];
            }
            else
            {
                if (x==root) tr[x][i]=root;
                else tr[x][i]=tr[fail[x]][i];
            }
    }
}
int dp(int len,int pos,bool edg,bool zero)
{
    if (len>tot) return 1;
    if (vis[len][pos][edg][zero]) return f[len][pos][edg][zero];
    int temp=pos;
    vis[len][pos][edg][zero]=1;
    for (int i=0;i<=(edg?num[len]-'0':9);i++)
    {   
        temp=pos;
        if (i==0&&zero) f[len][pos][edg][zero]=(f[len][pos][edg][zero]+dp(len+1,temp,edg&&(i==num[len]-'0'),zero))%mod;
        else
        {
            p=0;
            for (int j=0;j<le[i];j++)
            {
                temp=tr[temp][a[i][j]];
                if (tag[temp]) {p=1;break;}
            }
            if (!p) f[len][pos][edg][zero]=(f[len][pos][edg][zero]+dp(len+1,temp,edg&&(i==num[len]-'0'),0))%mod;
        }
    }
    return f[len][pos][edg][zero];
}
void dfs(int x,int fa,int sum)
{
    tag[x]=sum; 
    for (int i=head[x];i;i=e[i].next)
    {
        int y=e[i].to;
        dfs(y,x,sum|tag[y]);
    }
}
bool pan()
{
    int nw=root;
    for (int i=1;i<=tot;i++)
        for (int j=0;j<le[num[i]-'0'];j++)
        {
            nw=tr[nw][a[num[i]-'0'][j]];
            if (tag[nw]) return 0;
        }
    return 1;
}
int main()
{
//  freopen("3494.in","r",stdin);
//  freopen("3494.out","w",stdout);
    a[0][0]=0;a[0][1]=0;a[0][2]=0;a[0][3]=0;le[0]=4;
    a[1][0]=0;a[1][1]=0;a[1][2]=0;a[1][3]=1;le[1]=4;
    a[2][0]=0;a[2][1]=0;a[2][2]=1;a[2][3]=0;le[2]=4;
    a[3][0]=0;a[3][1]=0;a[3][2]=1;a[3][3]=1;le[3]=4;
    a[4][0]=0;a[4][1]=1;a[4][2]=0;a[4][3]=0;le[4]=4;
    a[5][0]=0;a[5][1]=1;a[5][2]=0;a[5][3]=1;le[5]=4;
    a[6][0]=0;a[6][1]=1;a[6][2]=1;a[6][3]=0;le[6]=4;
    a[7][0]=0;a[7][1]=1;a[7][2]=1;a[7][3]=1;le[7]=4;
    a[8][0]=1;a[8][1]=0;a[8][2]=0;a[8][3]=0;le[8]=4;
    a[9][0]=1;a[9][1]=0;a[9][2]=0;a[9][3]=1;le[9]=4;
    scanf("%d",&T);
    while (T--)
    {
        size=0;
        memset(head,0,sizeof(head));
        siz=1;
        tr[1][0]=tr[1][1]=tag[1]=fail[1]=0;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%s",s);
            insert(s);
        }
        get_fail();
        dfs(root,0,tag[root]);
        memset(f,0,sizeof(f));
        memset(vis,0,sizeof(vis));
        ans=0;
        scanf("%s",num+1);
        tot=strlen(num+1);
        ans-=dp(1,root,1,1);
        if (pan()) ans++;
        memset(f,0,sizeof(f));
        memset(vis,0,sizeof(vis));
        scanf("%s",num+1);
        tot=strlen(num+1);
        ans=(ans+dp(1,root,1,1)+mod)%mod;
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值