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

来源:互联网 发布:网络歌手东篱照片 编辑:程序博客网 时间:2024/04/30 11:23

题目大意:

就是现在有0~9对应的BCD码(就是对应的4位的二进制), 然后给出了n串(0 <= n <= 1000)不能出现的串(只包含0和1), 现在问在数字A到B之间有多少个数在转换成BCD码表示之后不包含这n个串,1 <= A <= B <= 10^200


大致思路:

首先考虑到A和B的范围,暴力是不可行的,这里需要用到数位DP,也就是逐位确定的思想,首先用AC自动机算出从状态i出发下一位是0~9分别可到达的状态,或者不可走,然后逐位确定各个数位的值即可,记忆化搜索提高效率,启示这个数位DP还是挺简单的,注意前导零的问题即可。


代码如下:

Result  :  Accepted     Memory  :  3676 KB     Time  :  210 ms

/* * Author: Gatevin * Created Time:  2014/11/27 10:18:15 * File Name: Kagome.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;const lint mod = 1000000009LL;int n;char num[210];int bit[210];int bcd[2010][10];//bcd[i][j]表示从AC自动机的i状态,下一个数字是j会到达的状态,为-1表示不可走lint dp[2010][210];//dp[i][j]表示从AC自动机的i状态,接下来的j位没有边界和前导零限制时的方案数,用于记忆化struct Trie{    int next[2010][2], fail[2010];    bool end[2010];    int L, root;    int newnode()    {        for(int i = 0; i < 2; i++)            next[L][i] = -1;        end[L++] = 0;        return L - 1;    }    void init()    {        L = 0;        root = newnode();        return;    }    void insert(char *s)    {        int now = root;        for(; *s; s++)        {            if(next[now][*s - '0'] == -1)                next[now][*s - '0'] = newnode();            now = next[now][*s - '0'];        }        end[now] = 1;        return;    }    void build()    {        queue <int> Q;        fail[root] = root;        Q.push(root);        while(!Q.empty())        {            int now = Q.front();            Q.pop();            if(end[fail[now]]) end[now] = 1;//标记所有不能到达的点            for(int i = 0; i < 2; i++)                if(next[now][i] == -1)                    next[now][i] = now == root ? root : next[fail[now]][i];                else                {                    fail[next[now][i]] = now == root ? root : next[fail[now]][i];                    Q.push(next[now][i]);                }        }        return;    }};Trie AC;void BCD()//计算bcd数组{    for(int i = 0; i < AC.L; i++)        for(int j = 0; j < 10; j++)        {            bcd[i][j] = i;            if(AC.end[bcd[i][j]])            {                bcd[i][j] = -1;                continue;            }            for(int k = 3; k >= 0; k--)            {                if(j & (1 << k))                    bcd[i][j] = AC.next[bcd[i][j]][1];                else                    bcd[i][j] = AC.next[bcd[i][j]][0];                if(AC.end[bcd[i][j]])                {                    bcd[i][j] = -1;                    break;                }            }        }    return;}/* * 返回剩下的长度为L需要填充,当前在AC自动机的pos状态, * bound表示接下来这一位是否考虑上界bit限制 * lead表示之前是否已经有数字,即当前位填0是否有状态转移(前导零不转移) */lint dfs(int L, int pos, bool bound, bool lead)//写的不怎么优美....{    if(L == 0 && lead) return 1;    if(L == 0 && !lead) return 0;    if(dp[pos][L] != -1 && !bound && lead) return dp[pos][L];    lint ret = 0;    if(!lead)//没有前导零    {        ret = (ret + dfs(L - 1, pos, false, false)) % mod;//继续没有前导零            if(bound)//有上界限制            {                for(int i = 1; i < bit[L]; i++)//接下来填的数比当前位小,接下去就没有限制                    if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod;                if(bcd[pos][bit[L]] != -1) ret = (ret + dfs(L - 1, bcd[pos][bit[L]], true, true)) % mod;//有限制            }            else                for(int i = 1; i < 10; i++)//没有限制就随便填了,接下去也没有限制                    if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod;    }    else    {        if(bound)        {            for(int i = 0; i < bit[L]; i++)                if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod;            if(bcd[pos][bit[L]] != -1) ret = (ret + dfs(L - 1, bcd[pos][bit[L]], true, true)) % mod;        }        else            for(int i = 0; i < 10; i++)                if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod;    }    if(lead && dp[pos][L] == -1 && !bound) dp[pos][L] = ret;//记忆化    return ret;}//计算从0到当前数字有多少是不包含那N个危险串的lint solve(int len){    if(len == 0) return 0;    for(int i = 0; i < len; i++)        bit[len - i] = num[i] - '0';    return dfs(len, AC.root, true, false);}int main(){    int t;    char ts[22];    scanf("%d", &t);    while(t--)    {        scanf("%d", &n);        AC.init();        while(n--)        {            scanf("%s", ts);            AC.insert(ts);        }        AC.build();        BCD();        scanf("%s", num);        int len = strlen(num);        /*         * 将当前的左界减去1         */        if(num[len - 1] > '0')        {            int tmp = len - 1;            if(len == 1 && num[len - 1] == '1') len = 0;            num[tmp] = num[tmp] - 1;        }        else        {            num[len - 1] = '9';            int now = len - 2;            while(num[now] == '0')            {                num[now] = '9';                now--;            }            if(num[now] == '1' && now == 0)            {                for(int i = 0; i < len; i++)                    num[i] = num[i + 1];                len--;            }            else                num[now] = num[now] - 1;        }        memset(dp, -1, sizeof(dp));        lint A = solve(len);        scanf("%s", num);        len = strlen(num);        lint B = solve(len);        printf("%lld\n", (B - A + mod) % mod);    }    return 0;}


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 家里养花生虫子怎么办 生鸡蛋拌饭怎么办 出柜家里人会怎么办 百灵鸟怕人撞笼怎么办 乌鸫雏鸟撞笼返生怎么办 泥石流来了怎么办教案 墙壁插座不通电怎么办 当前目录不能用怎么办 海鲜过敏怎么办没有药 哺乳后胸变小怎么办 有脑出血前兆该怎么办 体检发现甲状腺结节怎么办 月子里宝宝感冒怎么办 10天新生儿感冒怎么办 20天新生儿感冒怎么办 新生儿20天鼻塞怎么办 25天新生儿感冒怎么办 42天新生儿感冒怎么办 上呼吸道感染怎么办比较好 小猫咪太调皮怎么办 水晶彩泥弄到衣服上怎么办 进境动植物检疫许可怎么办 跳舞不会听拍子怎么办 税盘丢了注销公司怎么办 认缴资金不到位怎么办 同一单元有凶宅怎么办 有地皮没房产证怎么办 社保资金被侵吞怎么办? 集体计件手脚慢怎么办 发票当月没用完怎么办 非工业用地怎么办环评 商标注册途中英文错误怎么办 孩子的英文不好怎么办 高盛英文不好怎么办 去美国英文不好怎么办 去越南不会英语怎么办 法斗得了毛囊炎怎么办 头发里有毛囊炎怎么办 笔记本画cad慢怎么办 面试打不出问题怎么办 ai撤销多了怎么办