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

原文地址:https://www.cnblogs.com/heyuhhh/p/10877785.html