【SDOI2014】【BZOJ3530】【LOJ2194】数数

【题目链接】

【前置技能】

  • AC自动机
  • 数位DP

【题解】

  • 从题面中题面可以比较显然地看出算法:用数位DP解决计算范围内数的情况,而限制是一个字符串集合的匹配,用AC自动机解决。
  • 在AC自动机的每个节点上记录一个 t a g 表示在该节点到根的fail树上的节点中是否存在集合中的串的结尾节点。那么 t a g = 1 时状态不合法,否则合法。
  • 设计状态 f [ k ] [ p ] [ 0 / 1 ] 表示现在处理到第 k 位,在自动机上匹配到状态 p ,目前的数是否小于 N ,有多少个数。转移的时候枚举下一个数位填什么数,在自动机上走,然后转移即可。
  • 时间复杂度 O ( L e n N L e n S )

【代码】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN    2010
#define mod 1000000007
using namespace std;
int m;
char s[MAXN], t[MAXN];

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

void update(int &x, int y){
    x = (x + y) % mod;
}

struct AC_Automaton{
    struct info{int son[10], fail, tag;}a[MAXN];
    int cnt, root, f[MAXN][MAXN], g[MAXN], step[MAXN];
    void init(){
        cnt = root = 0;
    }
    void insert(char *s){
        int len = strlen(s + 1), pos = root;
        for (int i = 1; i <= len; ++i){
            int opt = s[i] - '0';
            if (!a[pos].son[opt]) a[pos].son[opt] = ++cnt;
            pos = a[pos].son[opt];
        }
        a[pos].tag = 1;
    }
    void build(){
        static int q[MAXN], l, r;
        l = 0, r = -1;
        for (int i = 0; i < 10; ++i)
            if (a[root].son[i]) q[++r] = a[root].son[i];
        while (l <= r){
            int pos = q[l++];
            for (int i = 0; i < 10; ++i){
                int son = a[pos].son[i];
                if (son) {
                    a[son].fail = a[a[pos].fail].son[i];
                    q[++r] = son;
                } else a[pos].son[i] = a[a[pos].fail].son[i];
            }
            a[pos].tag |= a[a[pos].fail].tag; 
        }
    }
    int nxt(int x, int ch){
        return a[x].son[ch];
    }
    int query(){
        int n = strlen(s + 1), pos = root;
        g[0] = 1;
        for (int i = 1; i <= n; ++i){
            pos = nxt(pos, s[i] - '0');
            step[i] = pos;
            if (a[pos].tag) g[i] = 0;
            else g[i] = g[i - 1];
        }
        for (int i = 1; i < s[1] - '0'; ++i)
            if (!a[nxt(root, i)].tag) ++f[1][nxt(root, i)];
        for (int i = 1; i < n; ++i){
            for (int j = 1; j <= 9; ++j)
                if (!a[nxt(root, j)].tag) ++f[i + 1][nxt(root, j)];
            for (int j = 0; j < s[i + 1] - '0'; ++j)
                if (!a[nxt(step[i], j)].tag) update(f[i + 1][nxt(step[i], j)], g[i]);
            for (int j = 0; j <= cnt; ++j){
                if (a[j].tag || f[i][j] == 0) continue;
                for (int k = 0; k <= 9; ++k){
                    int p = nxt(j, k); if (a[p].tag) continue;
                    update(f[i + 1][p], f[i][j]);
                }
            }
        }
        int ret = g[n];
        for (int i = 0; i <= cnt; ++i)
            if (!a[i].tag) update(ret, f[n][i]);
        return ret;
    }
}acam; 

int main(){
    scanf("%s", s + 1);
    read(m);
    acam.init();
    for (int i = 1; i <= m; ++i){
        scanf("%s", t + 1);
        acam.insert(t);
    }
    acam.build();
    printf("%d\n", acam.query());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/six_solitude/article/details/80765250