• P3311 [SDOI2014]数数


    题意
    我们称一个正整数N((len(N) <= 1200))是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。给定N和S,计算不大于N的幸运数个数。
    题解
    (Trie)图上跑数位(dp)(dp[i][j]) 表示走了(i)步到了结点(j)的方案数,那么转移的时候分两种情况,①是已经沿着边界走了(i)步,②不是沿着边界走了(i)步。多想想数位(dp)的边界处理方式。然后顺着图转移就行了。从高位向低位枚举数位的过程中会产生前导0,这与{0},{00},{000}会产生冲突,所以是前导0并且当前枚举的 i == 0, 就直接回到根节点
    代码

    const int mod = 1000000007;
    
    struct node {
        bool flag;
        int fail, vis[10];
        node() {
            mem(vis, 0);
            flag = fail = 0;
        }
    };
    
    node a[2000];
    
    struct Acmation {
        int tot;
    
        stack<int> st;
    
        void Inite() {
            tot = 0;
        }
        void Insert(char *s) {
            int n = strlen(s);
            int now = 0;
            rep(i, 0, n) {
                int id = s[i] - '0';
                if (!a[now].vis[id]) a[now].vis[id] = ++tot;
                now = a[now].vis[id];
            }
            a[now].flag = 1;
        }
        void getFail() {
            queue<int> q;
            rep(i, 0, 10) if (a[0].vis[i]) {
                a[a[0].vis[i]].fail = 0;
                q.push(a[0].vis[i]);
            }
            while(!q.empty()) {
                int now = q.front();
                q.pop();
                rep(i, 0, 10) {
                    int pre = a[a[now].fail].vis[i];
                    if (a[now].vis[i]) {
                        a[a[now].vis[i]].fail = pre;
                        a[a[now].vis[i]].flag |= a[pre].flag;
                        q.push(a[now].vis[i]);
                    }
                    else a[now].vis[i] = pre;
                }
            }
        }
    };
    
    Acmation ac;
    
    char s[2000];
    int n, m, dp[1300][1600];
    
    int DFS(int pos, int now, bool limit, bool lead) {
        if (pos == n) return 1;
        if (!limit && !lead && dp[pos][now] != -1) return dp[pos][now];
    
        int up = (limit ? s[pos] - '0' : 9);
        int ans = 0;
    
        Rep(i, 0, up) {
            if (!i && lead) ans = (ans + DFS(pos + 1, 0, 0, 1)) % mod;
            else {
                if (a[a[now].vis[i]].flag) continue;
                ans = (ans + DFS(pos + 1, a[now].vis[i], limit && (i == up), lead && (i == 0))) % mod;
            }
        }
    
        if (!limit && !lead) dp[pos][now] = ans;
        return ans;
    }
    
    int main()
    {
        ac.Inite();
    
        scanf("%s", s);
        n = strlen(s);
    
        char str[2000];
    
        sc(m);
        Rep(i, 1, m) {
            scanf("%s", str);
            ac.Insert(str);
        }
    
        ac.getFail();
    
        mem(dp, -1);
        pr(DFS(0, 0, 1, 1) - 1);
    }
    
    
  • 相关阅读:
    IE下CSS属性float:right下换行问题解决方法
    php 中简单输出 csv和excel
    VMware 链接网络的三种模式及自己的安装方法
    ajax的应用
    php中ADODB的用法
    关于web 标准的常见问题 总结
    javascript 闭包
    php strrev 中文字符串翻转乱码的问题
    注册表 一览
    SVN Commit报错 svn: E155037: Previous operation has not finished; run 'cleanup' if it was interrupted
  • 原文地址:https://www.cnblogs.com/zgglj-com/p/9733936.html
Copyright © 2020-2023  润新知