【题解】SDOI2014数数

真的很开心呢,总算是有一道完完全全由自己做出来的题目啦~

这一道题目洛谷P3311和另一道JSOI文本生成器的题目是十分相像的,dp方面几乎相同。只是<=n的约束,让这道题目必须结合数位dp的方法,新建一个维度代表之后数字的大小是否受到限制。0代表受限,1代表不受限。但是处理前导零的部分的确较为头痛,最后采取的方法是在第一次dp的时候不允许有前导零的存在,在第二次dp的时候才把这一部分的答案统计出来。所以在第二次的dp转移中,一个节点只能由父亲节点、而不能由Fail节点转移而来。

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000
#define mod 1000000007
int m, cnt = 1, len, ans, ch[maxn][10], dp[maxn][maxn][2], fail[maxn];
bool error[maxn];
string n, s;
struct AC_Automation
{
    void Trie_ins()
    {
        int now = 1, len = s.length(); 
        for(int i = 0; i < len; i ++)
        {
            if(!ch[now][s[i] - '0']) ch[now][s[i] - '0'] = ++ cnt;
            now = ch[now][s[i] - '0'];
        }
        error[now] = true;
    }
    
    void AC_build()
    {
        queue <int> q;
        q.push(1);
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            for(int i = 0; i < 10; i ++)
            {
                if(ch[u][i])
                {
                    int v = ch[u][i], k = fail[u];
                    while(!ch[k][i]) k = fail[k];
                    fail[v] = ch[k][i];
                    if(error[fail[v]]) error[v] = true;
                    q.push(v);
                }
                else ch[u][i] = ch[fail[u]][i];
            }
        }
    }
}ACM;

void DP(int x)
{
    for(int i = 1; i <= cnt; i ++)
    {
        if(error[i] || (!dp[x - 1][i][0] && !dp[x - 1][i][1])) continue;
        for(int j = 0; j < 10; j ++)
        {
            if(x == 1 && j == 0) continue;
            int k = i;
            while(!ch[k][j]) k = fail[k];
            int v = ch[k][j];
            if(j == n[x - 1] - '0') dp[x][v][0] = (dp[x][v][0] + dp[x - 1][i][0]) % mod;
            if(j < n[x - 1] - '0') dp[x][v][1] = (dp[x][v][1] + dp[x - 1][i][0]) % mod;
            dp[x][v][1] = (dp[x][v][1] + dp[x - 1][i][1]) % mod;
        }
    }
}

void DP2()
{
    dp[0][1][1] = 1;
    for(int x = 1; x < len; x ++)
        for(int i = 1; i <= cnt; i ++)
        {
            if(error[i] || !dp[x - 1][i][1]) continue;
            for(int j = 0; j < 10; j ++)
            {
                if(x == 1 && j == 0) continue;
                int k = i;
                int v = ch[k][j];
                dp[x][v][1] = (dp[x][v][1] + dp[x - 1][i][1]) % mod;
            }
        }
    for(int i = 1; i < len; i ++)
        for(int j = 1; j <= cnt; j ++)
            if(!error[j]) ans = (ans + dp[i][j][1]) % mod;
}

int main()
{
    cin >> n;
    cin >> m;
    for(int i = 0; i < 10; i ++) ch[0][i] = 1;
    for(int i = 1; i <= m; i ++)
    {
        cin >> s;
        ACM.Trie_ins();
    }
    ACM.AC_build();
    len = n.length();
    dp[0][1][0] = 1;
    for(int i = 1; i <= len; i ++) DP(i);
    for(int i = 1; i <= cnt; i ++)
        if(!error[i]) ans = (ans + dp[len][i][0] + dp[len][i][1]) % mod;
    memset(dp, 0, sizeof(dp));
    DP2();
    printf("%d\n", ans);
    return 0;
}

 

posted @ 2018-02-20 18:12  Twilight_Sx  阅读(266)  评论(0编辑  收藏  举报