[Luogu P3311] [BZOJ 3530] [SDOI2014]數數
阿新 • • 發佈:2018-11-25
洛谷傳送門
BZOJ傳送門
題目描述
我們稱一個正整數 是幸運數,當且僅當它的十進位制表示中不包含數字串集合 中任意一個元素作為其子串。例如當 時, 是幸運數, 、 、 不是幸運數。 給定 和 ,計算不大於 的幸運數個數。
輸入輸出格式
輸入格式:
輸入的第一行包含整數 。 接下來一行一個整數 ,表示 中元素的數量。 接下來 行,每行一個數字串,表示 中的一個元素。
輸出格式:
輸出一行一個整數,表示答案模 的值。
輸入輸出樣例
輸入樣例#1:
20
3
2
3
14
輸出樣例#1:
14
說明
下表中 表示 的長度, 表示 中所有串長度之和。
解題分析
一眼 自動機上 。 再仔細一看, 似乎有點數位 的味道?那就模仿數位 再記一個是否達到上界就可以了。
特別注意不能出現的串可能有字首 , 然後形如 實際上是沒有字首 的, 所以可以在 號節點特判而不特殊考慮字首 。
程式碼如下:
#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();
}