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);
}

posted @ 2018-10-01 01:29  天之道,利而不害  阅读(237)  评论(0编辑  收藏  举报