首页 > 代码库 > zoj 3494 BCD Code(AC自动机+数位dp)

zoj 3494 BCD Code(AC自动机+数位dp)

题目链接:zoj 3494 BCD Code

题目大意:给定n个2进制串,然后有一个区间l,r,问说l,r之间有多少个数转换成BCD二进制后不包含上面的2进制串。

解题思路:AC自动机+数位dp。先对禁止串建立AC自动机,所有的单词节点即为禁止通行的节点。接着进行数位dp,

用solve(r) - solve(l-1), 这里的l需要用到大数减法。dp[i][j]表示第i为移动到节点j的可行方案数,每次枚举下一位数字,因

为是BCD二进制,所以每位数要一次性移动4个字符,中途有经过禁止点都是不行的。然后用一个eq,mv标记相等的情

况,相等的情况也有可能是不可以的,即eq = 0时。注意处理前导0的情况,因为有些禁止串可以由0组成,所以对于前

导0不能在AC自动机上移动,在每一位的时候单独拿出来考虑。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 2005;
const int mod = 1e9 + 9;
const int sigma_size = 2;
const char sign[12][5] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001"};

struct Aho_Corasick {
    int sz, g[maxn][sigma_size];
    int tag[maxn], fail[maxn], last[maxn];

    int dp[205][maxn];

    void init();
    int idx(char ch);
    void insert(char* str, int k);
    void getFail();
    void match(char* str);
    void put(int x, int y);
    int solve(int* a, int n);
    int move(int u, int v);
}AC;

int N, num[205];

int getNum(int* a) {
    char s[205];
    scanf("%s", s);
    int n = strlen(s), mv = 0;
    while (mv < n && s[mv] == ‘0‘) mv++;

    n -= mv;
    if (n == 0)
        num[n++] = 0;
    else {
        for (int i = 0; i < n; i++)
            num[i] = s[i+mv] - ‘0‘;
    }
    return n;
}

void del(int* a, int& n) {
    if (n == 0)
        return;

    a[n-1]--;
    for (int i = n-1; i >= 0; i--) {
        if (a[i] < 0) {
            a[i] += 10;
            a[i-1]--;
        } else
            break;
    }

    if (a[0] == 0) {
        for (int i = 0; i < n; i++)
            a[i] = a[i+1];
        n--;
    }
}

int main () {
    int cas, n;
    char w[50];
    scanf("%d", &cas);

    while (cas--) {
        AC.init();
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%s", w);
            AC.insert(w, i);
        }
        AC.getFail();

        N = getNum(num);
        del(num, N);
        int l = AC.solve(num, N);

        N = getNum(num);
        int r = AC.solve(num, N);

        printf("%d\n", (r - l + mod) % mod);
    }
    return 0;
}

int Aho_Corasick::move(int u, int id) {
    for (int i = 0; i < 4; i++) {
        int v = idx(sign[id][i]);
        while (u && g[u][v] == 0)
            u = fail[u];
        u = g[u][v];

        if (tag[u] || last[u])
            return -1;
    }
    return u;
}

int Aho_Corasick::solve(int* a, int n) {
    if (n == 0)
        return 0;
    memset(dp, 0, sizeof(dp));

    int eq = 1, mv = 0;
    for (int i = 0; i < n; i++) {
        for (int x = 0; x < sz; x++) {
            if (tag[x] || last[x])
                continue;

            for (int j = 0; j < 10; j++) {
                int u = move(x, j);
                if (u == -1)
                    continue;
                dp[i+1][u] = (dp[i+1][u] + dp[i][x]) % mod;
            }
        }

        if (i) {
            for (int j = 1; j < 10; j++) {
                int u = move(0, j);
                if (u == -1)
                    continue;
                dp[i+1][u] = (dp[i+1][u] + 1) % mod;
            }
        }

        if (eq) {
            for (int j = (i == 0 ? 1 : 0); j < a[i]; j++) {
                int u = move(mv, j);
                if (u == -1)
                    continue;
                dp[i+1][u] = (dp[i+1][u] + 1) % mod;
            }

            int u = move(mv, a[i]);
            if (u == -1) eq = 0;
            mv = u;
        }
    }

    int ans = eq;
    for (int i = 0; i < sz; i++) {
        if (tag[i] || last[i])
            continue;
        ans = (ans + dp[n][i]) % mod;
    }
    return ans;
}

void Aho_Corasick::init() {
    sz = 1;
    tag[0] = 0;
    memset(g[0], 0, sizeof(g[0]));
}

int Aho_Corasick::idx(char ch) {
    return ch - ‘0‘;
}

void Aho_Corasick::put(int x, int y) {
}

void Aho_Corasick::insert(char* str, int k) {
    int u = 0, n = strlen(str);

    for (int i = 0; i < n; i++) {
        int v = idx(str[i]);
        if (g[u][v] == 0) {
            tag[sz] = 0;
            memset(g[sz], 0, sizeof(g[sz]));
            g[u][v] = sz++;
        }
        u = g[u][v];
    }
    tag[u] = k;
}

void Aho_Corasick::match(char* str) {
    int n = strlen(str), u = 0;
    for (int i = 0; i < n; i++) {
        int v = idx(str[i]);
        while (u && g[u][v] == 0)
            u = fail[u];

        u = g[u][v];

        if (tag[u])
            put(i, u);
        else if (last[u])
            put(i, last[u]);
    }
}

void Aho_Corasick::getFail() {
    queue<int> que;

    for (int i  = 0; i < sigma_size; i++) {
        int u = g[0][i];
        if (u) {
            fail[u] = last[u] = 0;
            que.push(u);
        }
    }

    while (!que.empty()) {
        int r = que.front();
        que.pop();

        for (int i = 0; i < sigma_size; i++) {
            int u = g[r][i];

            if (u == 0) {
                g[r][i] = g[fail[r]][i];
                continue;
            }

            que.push(u);
            int v = fail[r];
            while (v && g[v][i] == 0)
                v = fail[v];

            fail[u] = g[v][i];
            last[u] = tag[fail[u]] ? fail[u] : last[fail[u]];
        }
    }
}

zoj 3494 BCD Code(AC自动机+数位dp)