链接:https://ac.nowcoder.com/acm/problem/20366
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。
给定N和S,计算不大于N的幸运数个数。
输入描述:
输入的第一行包含整数N。 接下来一行一个整数M,表示S中元素的数量。 接下来M行,每行一个数字串,表示S中的一个元素。
输出描述:
输出一行一个整数,表示答案模109+7的值。
示例1
输入
复制20 3 2 3 14
20 3 2 3 14
输出
复制14
14
题意:
给出一个n,询问小于n的正整数中有多少数字,其十进制中不含有给出的m个串。
做法:
看起来是个很套路的题,先对m个串建一个AC自动机,然后再数位dp求解,每一层都在遍历
字典树,判断这个点极其后缀中是否含有m个串中的某一个即可,唯一坑点是给出的m个串
可能带有前导0。
比如样例
20
1
03
正确答案是20
所以定状态时需要多加一维状态表示是否有前导0。
#include <bits/stdc++.h>
#include <random>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#ifdef LOCAL
#define debug(x) cout << "[" __FUNCTION__ ": " #x " = " << (x) << "]\n"
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
#define hash_ 1000000009
#define Continue(x) { x; continue; }
#define Break(x) { x; break; }
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
#define gc p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
inline int read(){ static char buf[1000000], *p1 = buf, *p2 = buf; register int x = false; register char ch = gc; register bool sgn = false; while (ch != '-' && (ch < '0' || ch > '9')) ch = gc; if (ch == '-') sgn = true, ch = gc; while (ch >= '0'&& ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc; return sgn ? -x : x; }
ll fpow(ll a, int b, int mod) { ll res = 1; for (; b > 0; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; }
char s[N];
char x[N];
char a[N];
int nxt[N][10];
int tot;
int sed[N];
int fail[N];
void insert(char* s)
{
int rk = 0;
char* x = s;
while (*x)
{
int now = *x - '0';
if (nxt[rk][now] == 0)
nxt[rk][now] = ++tot;
rk = nxt[rk][now];
x++;
}
sed[rk]++;
}
void bulid()
{
queue<int>q;
for (int i = 0; i < 10; i++)
if (nxt[0][i])
q.push(nxt[0][i]);
while (!q.empty())
{
int f = q.front();
q.pop();
sed[f] |= sed[fail[f]]; //可以O1查询其后缀中是否含有非法
for (int i = 0; i < 10; i++)
if (nxt[f][i])
fail[nxt[f][i]] = nxt[fail[f]][i], q.push(nxt[f][i]);
else
nxt[f][i] = nxt[fail[f]][i];
}
}
int d[1205][2][1510][2];
int dfs(int pos, int lim, int rk, int zero)
{
if (pos == 0)
return zero == 0;
if (d[pos][lim][rk][zero] != -1)
return d[pos][lim][rk][zero];
int up = lim ? a[pos] : 9;
int sum = 0;
for (int i = 0; i <= up; i++)
{
int nrk = (zero && i == 0) ? 0 : nxt[rk][i];
if (sed[nrk])
continue;
sum = (sum + dfs(pos - 1, i == up && lim, nrk, zero && i == 0)) % mod;
}
return d[pos][lim][rk][zero] = sum;
}
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
memset(d, -1, sizeof d);
int cnt = 0;
scanf("%s", a + 1);
int n = strlen(a + 1);
reverse(a + 1, a + n + 1);
for (int i = 1; i <= n; i++)
{
a[i] -= '0';
}
int m;
cin >> m;
for (int i = 1; i <= m; i++)
{
scanf("%s", s);
insert(s);
}
bulid();
cout << dfs(n, 1, 0, 1) << endl;
return TIME;
}