1. 程式人生 > >[Luogu P3311] [BZOJ 3530] [SDOI2014]數數

[Luogu P3311] [BZOJ 3530] [SDOI2014]數數

洛谷傳送門

BZOJ傳送門

題目描述

我們稱一個正整數 N N 是幸運數,當且僅當它的十進位制表示中不包含數字串集合 S S 中任意一個元素作為其子串。例如當 S

= ( 22 333 0233 ) S=(22,333,0233) 時, 233 233 是幸運數, 2333 2333 20233 20233
3223 3223 不是幸運數。 給定 N N S S ,計算不大於 N N 的幸運數個數。

輸入輸出格式

輸入格式:

輸入的第一行包含整數 N N 。 接下來一行一個整數 M M ,表示 S S 中元素的數量。 接下來 M M 行,每行一個數字串,表示 S S 中的一個元素。

輸出格式:

輸出一行一個整數,表示答案模 1 0 9 + 7 10^9+7 的值。

輸入輸出樣例

輸入樣例#1:

20
3
2
3
14

輸出樣例#1:

14

說明

下表中 l l 表示 N N 的長度, L L 表示 S S 中所有串長度之和。

1 l 1200 , 1 M 100 , 1 L 1500 1 \le l \le 1200 , 1 \le M \le 100 ,1 \le L \le 1500

解題分析

一眼 A C AC 自動機上 d p dp 。 再仔細一看, 似乎有點數位 d p dp 的味道?那就模仿數位 d p dp 再記一個是否達到上界就可以了。

特別注意不能出現的串可能有字首 0 0 , 然後形如 000058 000058 實際上是沒有字首 0 0 的, 所以可以在 0 0 號節點特判而不特殊考慮字首 0 0

程式碼如下:

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1505
#define MOD 1000000007
IN void add(R int ad, int &tar) {tar += ad; if (tar > MOD) tar -= MOD;}
int n, cnt, root;
char buf[MX], mod[MX];
int son[MX][10], fail[MX], dp[MX][MX][2];
bool tag[MX];
std::queue <int> q;
void insert(char *str)
{
    R int len = std::strlen(str), now = root, id;
    for (R int i = 0; i < len; ++i)
    {
        id = str[i] - '0';
        if (!son[now][id]) son[now][id] = ++cnt;
        now = son[now][id];
    }
    tag[now] = true;
}
void build()
{
    R int now, tar;
    for (R int i = 0; i < 10; ++i) if (son[0][i]) q.push(son[0][i]);
    W (!q.empty())
    {
        now = q.front(); q.pop();
        for (R int i = 0; i < 10; ++i)
        if (son[now][i])
        {
            q.push(son[now][i]); tar = fail[now];
            fail[son[now][i]] = son[tar][i];
            tag[son[now][i]] |= tag[son[tar][i]];
        }
        else son[now][i] = son[fail[now]][i];
    }
}
void DP()
{
    R int nex, len = std::strlen(mod + 1), lim;
    R int i, j, k;
    for (i = 0; i < len; ++i)
    {
        nex = i + 1, lim = mod[nex] - '0';
        for (j = 0; j <= cnt; ++j)
        {
            if (dp[i][j][1])//limited
            {
                for (k = 0; k < lim; ++k) if (!tag[son[j][k]])
                add(dp[i][j][1], dp[nex][son[j][k]][0]);
                if (!tag[son[j][lim]])
                add(dp[i][j][1], dp[nex][son[j][lim]][1]);
            }
            if (dp[i][j][0])//unlimited
            {
                for (k = 0; k < 10; ++k) if (!tag[son[j][k]])
                add(dp[i][j][0], dp[nex][son[j][k]][0]);
            }
            if (!j)//root
            {
                if (!i)
                {
                    for (k = 1; k < lim; ++k) if (!tag[son[j][k]]) add(1, dp[nex][son[j][k]][0]);
                    if (!tag[son[j][lim]]) add(1, dp[nex][son[j][lim]][1]);
                }
                else
                {
                    for (k = 1; k < 10; ++k) if (!tag[son[j][k]])
                    add(1, dp[nex][son[j][k]][0]);
                }
            }
        }
    }
    int ans = 0;
    for (R int i = 0; i <= cnt; ++i) add(dp[len][i][0], ans), add(dp[len][i][1], ans);
    printf("%d", ans);
}
int main(void)
{
    scanf("%s%d", mod + 1, &n);
    W (n--) scanf("%s", buf), insert(buf);
    build(), DP();
}