【题目链接】
【前置技能】
- AC自动机
- 数位DP
【题解】
- 从题面中题面可以比较显然地看出算法:用数位DP解决计算范围内数的情况,而限制是一个字符串集合的匹配,用AC自动机解决。
- 在AC自动机的每个节点上记录一个 tag t a g 表示在该节点到根的fail树上的节点中是否存在集合中的串的结尾节点。那么 tag=1 t a g = 1 时状态不合法,否则合法。
- 设计状态 f[k][p][0/1] f [ k ] [ p ] [ 0 / 1 ] 表示现在处理到第 k k 位,在自动机上匹配到状态,目前的数是否小于 N N ,有多少个数。转移的时候枚举下一个数位填什么数,在自动机上走,然后转移即可。
- 时间复杂度
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 2010
#define mod 1000000007
using namespace std;
int m;
char s[MAXN], t[MAXN];
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
void update(int &x, int y){
x = (x + y) % mod;
}
struct AC_Automaton{
struct info{int son[10], fail, tag;}a[MAXN];
int cnt, root, f[MAXN][MAXN], g[MAXN], step[MAXN];
void init(){
cnt = root = 0;
}
void insert(char *s){
int len = strlen(s + 1), pos = root;
for (int i = 1; i <= len; ++i){
int opt = s[i] - '0';
if (!a[pos].son[opt]) a[pos].son[opt] = ++cnt;
pos = a[pos].son[opt];
}
a[pos].tag = 1;
}
void build(){
static int q[MAXN], l, r;
l = 0, r = -1;
for (int i = 0; i < 10; ++i)
if (a[root].son[i]) q[++r] = a[root].son[i];
while (l <= r){
int pos = q[l++];
for (int i = 0; i < 10; ++i){
int son = a[pos].son[i];
if (son) {
a[son].fail = a[a[pos].fail].son[i];
q[++r] = son;
} else a[pos].son[i] = a[a[pos].fail].son[i];
}
a[pos].tag |= a[a[pos].fail].tag;
}
}
int nxt(int x, int ch){
return a[x].son[ch];
}
int query(){
int n = strlen(s + 1), pos = root;
g[0] = 1;
for (int i = 1; i <= n; ++i){
pos = nxt(pos, s[i] - '0');
step[i] = pos;
if (a[pos].tag) g[i] = 0;
else g[i] = g[i - 1];
}
for (int i = 1; i < s[1] - '0'; ++i)
if (!a[nxt(root, i)].tag) ++f[1][nxt(root, i)];
for (int i = 1; i < n; ++i){
for (int j = 1; j <= 9; ++j)
if (!a[nxt(root, j)].tag) ++f[i + 1][nxt(root, j)];
for (int j = 0; j < s[i + 1] - '0'; ++j)
if (!a[nxt(step[i], j)].tag) update(f[i + 1][nxt(step[i], j)], g[i]);
for (int j = 0; j <= cnt; ++j){
if (a[j].tag || f[i][j] == 0) continue;
for (int k = 0; k <= 9; ++k){
int p = nxt(j, k); if (a[p].tag) continue;
update(f[i + 1][p], f[i][j]);
}
}
}
int ret = g[n];
for (int i = 0; i <= cnt; ++i)
if (!a[i].tag) update(ret, f[n][i]);
return ret;
}
}acam;
int main(){
scanf("%s", s + 1);
read(m);
acam.init();
for (int i = 1; i <= m; ++i){
scanf("%s", t + 1);
acam.insert(t);
}
acam.build();
printf("%d\n", acam.query());
return 0;
}