洛谷传送门
BZOJ传送门
题目描述
我们称一个正整数 N N N是幸运数,当且仅当它的十进制表示中不包含数字串集合 S S S中任意一个元素作为其子串。例如当 S = ( 22 , 333 , 0233 ) S=(22,333,0233) S=(22,333,0233)时, 233 233 233是幸运数, 2333 2333 2333、 20233 20233 20233、 3223 3223 3223不是幸运数。 给定 N N N和 S S S,计算不大于 N N N的幸运数个数。
输入输出格式
输入格式:
输入的第一行包含整数 N N N。 接下来一行一个整数 M M M,表示 S S S中元素的数量。 接下来 M M M行,每行一个数字串,表示 S S S中的一个元素。
输出格式:
输出一行一个整数,表示答案模 1 0 9 + 7 10^9+7 109+7的值。
输入输出样例
输入样例#1:
20
3
2
3
14
输出样例#1:
14
说明
下表中 l l l表示 N N N的长度, L L L表示 S 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 1≤l≤1200,1≤M≤100,1≤L≤1500
解题分析
一眼 A C AC AC自动机上 d p dp dp。 再仔细一看, 似乎有点数位 d p dp dp的味道?那就模仿数位 d p dp dp再记一个是否达到上界就可以了。
特别注意不能出现的串可能有前缀 0 0 0, 然后形如 000058 000058 000058实际上是没有前缀 0 0 0的, 所以可以在 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();
}