• P3311 [SDOI2014]数数


    P3311 [SDOI2014]数数

    思路:

    因为这个题有多个串,所以我们可以考虑构建一个AC自动机,然后在AC自动机上面跑数位dp。
    (dp(i,j))表示当前是第(i)位(从高到低),在AC自动机上面的第(j)个结点的合法情况总数。
    同数位dp一样,看看当前这一位目前是否有限制,如果没限制,就是(dp(i+1,fail[ch[j][k]+=dp(i,j)),0leq kleq9);有限制的话给(k)加一个限制就好了。
    注意一下因为不能出现,所以我们在AC自动机的结点不能到单词末尾结点,这个判断一下就行了。
    对了,还要注意一个点就是要有后缀连接,比如对于串(abcd,bc),我们从(a)开始跑到(c)时,这时并不是单词某位结点,但(bc)是,这样的话其实我们也不能跑(c)这个结点的。当初这个点坑了我好几个小时。
    代码如下

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1505, MAX = 10, MOD = 1e9 + 7;
    char s[N], t[N];
    int n;
    int tot;
    int ch[N][MAX], fail[N * MAX];
    int lim[N] ;
    ll dp[N][N][2] ;
    bool f[N] ;
    void insert(char *t) {
        int L = strlen(t + 1) ;
        int u = 0;
        for(int i = 1 ;i <= L; i++) {
            int v = t[i] - '0' ;
            if(!ch[u][v]) ch[u][v] = ++tot;
            u = ch[u][v] ;
        }
        f[u] = 1;
    }
    void build_ac() {
        queue <int> q;
        for(int i = 0;i < MAX; i++) if(ch[0][i]) q.push(ch[0][i]), fail[ch[0][i]] = 0;
        while(!q.empty()) {
            int u = q.front(); q.pop() ;
            if(f[fail[u]]) f[u] = f[fail[u]] ;//后缀连接
            for(int i = 0; i < MAX; i++) {
                if(ch[u][i]) {
                    fail[ch[u][i]] = ch[fail[u]][i] ;
                    q.push(ch[u][i]) ;
                } else ch[u][i] = ch[fail[u]][i] ;
            }
        }
        ch[0][0] = 0;
    }
    void add(ll &x, ll y) {
        x += y;
        if(x >= MOD) x -= MOD ;
    }
    int main() {
        scanf("%s", s + 1);
        cin >> n;
        for(int i = 1; i <= n; i++) scanf("%s", t + 1), insert(t);
        build_ac();
        int L = strlen(s + 1) ;
        for(int i = 1; i <= L; i++) lim[L - i + 1] = s[i] - '0' ;
        dp[L + 1][0][1] = 1;
        for(int i = L + 1; i > 1; i--) {
            for(int j = 0; j <= tot; j++) {
                for(int l = 0; l < 2; l++) {
                    int up;
                    if(l == 0) up = 9;
                    else up = lim[i - 1] ;
                    for(int k = 0; k <= up; k++) {
                        int to = ch[j][k] ;
                        if(!f[to]) add(dp[i - 1][to][l & (k == up)], dp[i][j][l]) ;
                    }
                }
            }
        }
        ll ans = 0;
        for(int i = 0; i <= tot; i++) add(ans, dp[1][i][0]), add(ans, dp[1][i][1]);
        cout << ans - 1;
        return 0;
    }
    
    
  • 相关阅读:
    怎样从外网访问内网Django?
    怎样从外网访问内网Jboss?
    怎样从外网访问内网php-fpm?
    python中关于发邮件的示例
    python中关于局部变量与全局变量的认识
    python实现二分查找与冒泡排序
    自动化测试框架中关于selenium api的二次封装
    python 的日志相关应用
    python中关于字符串的操作
    EBS 物料单位换算
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10877785.html
Copyright © 2020-2023  润新知