P3311 [SDOI2014]数数

P3311 [SDOI2014]数数

思路:

因为这个题有多个串,所以我们可以考虑构建一个AC自动机,然后在AC自动机上面跑数位dp。
\(dp(i,j)\)表示当前是第\(i\)位(从高到低),在AC自动机上面的第\(j\)个结点的合法情况总数。
同数位dp一样,看看当前这一位目前是否有限制,如果没限制,就是\(dp(i+1,fail[ch[j][k]+=dp(i,j)),0\leq k\leq9\);有限制的话给\(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;
}

posted @ 2019-05-16 20:26  heyuhhh  阅读(184)  评论(0编辑  收藏  举报