把所有的模式串构建成一个AC自动机。
定义状态:
f[i][u]
f
[
i
]
[
u
]
表示到了第
i
i
位,走到AC自动机上的节点。
「走到AC自动机上的节点
u
u
」的具体含义:节点代表的串是数字串的一个后缀,并且是数字串的所有后缀中能被AC自动机识别(一个串
S
S
能被AC自动机识别意为AC自动机上存在一个节点代表串)的最长后缀。
怎样判断一个状态是否合法(不包含所有的模式串作为子串)呢?可以发现,对于一个状态
f[i][u]
f
[
i
]
[
u
]
,只需要保证节点
u
u
代表的字符串的所有后缀都不等于任何一个模式串。所以定义,表示节点
u
u
不断地往fail指针走,能否走到一个模式串的末尾。这时候只要满足,那么
f[i][u]
f
[
i
]
[
u
]
这个状态就是合法的。
然而由于要求不大于
N
N
,所以加上一维,进行数位DP:
:前
i
i
位小于的前
i
i
位。
:前
i
i
位等于的前
i
i
位。
:前
i
i
位大于的前
i
i
位。
转移即枚举第位的取值
x
x
,下面定的第
i+1
i
+
1
位的值为
y
y
,为在AC自动机上节点
u
u
通过为的边转移到的节点,分类讨论:
1、
x<y
x
<
y
时
f[i+1][v][0]+=f[i][u][0]+f[i][u][1]
f
[
i
+
1
]
[
v
]
[
0
]
+
=
f
[
i
]
[
u
]
[
0
]
+
f
[
i
]
[
u
]
[
1
]
f[i+1][v][2]+=f[i][u][2]
f
[
i
+
1
]
[
v
]
[
2
]
+
=
f
[
i
]
[
u
]
[
2
]
2、
x>y
x
>
y
时
f[i+1][v][2]+=f[i][u][2]+f[i][u][1]
f
[
i
+
1
]
[
v
]
[
2
]
+
=
f
[
i
]
[
u
]
[
2
]
+
f
[
i
]
[
u
]
[
1
]
f[i+1][v][0]+=f[i][u][0]
f
[
i
+
1
]
[
v
]
[
0
]
+
=
f
[
i
]
[
u
]
[
0
]
3、
x=y
x
=
y
时
f[i+1][v][0]+=f[i][u][0]
f
[
i
+
1
]
[
v
]
[
0
]
+
=
f
[
i
]
[
u
]
[
0
]
f[i+1][v][1]+=f[i][u][1]
f
[
i
+
1
]
[
v
]
[
1
]
+
=
f
[
i
]
[
u
]
[
1
]
f[i+1][v][2]+=f[i][u][2]
f
[
i
+
1
]
[
v
]
[
2
]
+
=
f
[
i
]
[
u
]
[
2
]
(注意第
1
1
位不能为)
最后结果:
∑l−1i=1∑u∑2k=0f[i][u][k]+∑u(f[l][u][0]+f[l][u][1])
∑
i
=
1
l
−
1
∑
u
∑
k
=
0
2
f
[
i
]
[
u
]
[
k
]
+
∑
u
(
f
[
l
]
[
u
]
[
0
]
+
f
[
l
]
[
u
]
[
1
]
)
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1550, MX = 1e9 + 7;
char s[N], ed[N];
int n, m, l, QAQ, len, que[N], f[N][N][3];
bool orz[N];
struct cyx {
int cnt, nxt, go[10];
void init() {
cnt = nxt = 0;
memset(go, 0, sizeof(go));
}
} T[N];
void ins() {
int i, u = 1; for (i = 1; i <= l; i++) {
int c = ed[i] - '0';
if (!T[u].go[c]) T[T[u].go[c] = ++QAQ].init();
u = T[u].go[c];
}
T[u].cnt++;
}
void buildFail() {
int i, c; for (i = que[len = 1] = 1; i <= len; i++) {
int u = que[i];
for (c = 0; c < 10; c++) {
int v = T[u].go[c]; if (!v) {
T[u].go[c] = T[T[u].nxt].go[c];
continue;
}
int w = T[u].nxt; while (!T[w].go[c]) w = T[w].nxt;
T[v].nxt = T[w].go[c]; que[++len] = v;
orz[v] = (T[v].cnt > 0) || orz[T[v].nxt];
}
}
}
int cx(int i, int j) {
if (i < j) return 0; if (i > j) return 2; return 1;
}
void DP() {
int i, u, c; for (i = 1; i < 10; i++) {
u = T[1].go[i]; if (!orz[u])
f[1][u][cx(i, s[1] - '0')]++;
}
for (i = 1; i < n; i++) for (u = 1; u <= QAQ; u++) if (!orz[u])
for (c = 0; c < 10; c++) {
int v = T[u].go[c], op = cx(c, s[i + 1] - '0');
if (!orz[v]) {
if (op == 0) {
f[i + 1][v][0] = (f[i + 1][v][0] + f[i][u][0]) % MX;
f[i + 1][v][0] = (f[i + 1][v][0] + f[i][u][1]) % MX;
f[i + 1][v][2] = (f[i + 1][v][2] + f[i][u][2]) % MX;
}
else if (op == 2) {
f[i + 1][v][2] = (f[i + 1][v][2] + f[i][u][2]) % MX;
f[i + 1][v][2] = (f[i + 1][v][2] + f[i][u][1]) % MX;
f[i + 1][v][0] = (f[i + 1][v][0] + f[i][u][0]) % MX;
}
else {
f[i + 1][v][1] = (f[i + 1][v][1] + f[i][u][1]) % MX;
f[i + 1][v][0] = (f[i + 1][v][0] + f[i][u][0]) % MX;
f[i + 1][v][2] = (f[i + 1][v][2] + f[i][u][2]) % MX;
}
}
}
}
int main() {
int i, j, ans = 0; scanf("%s", s + 1); n = strlen(s + 1); cin >> m;
T[QAQ = 1].init(); T[0].init(); for (i = 0; i < 10; i++) T[0].go[i] = 1;
for (i = 1; i <= m; i++)
scanf("%s", ed + 1), l = strlen(ed + 1), ins();
buildFail(); DP(); for (i = 1; i < n; i++) for (j = 1; j <= QAQ; j++) {
ans = (ans + f[i][j][0]) % MX; ans = (ans + f[i][j][1]) % MX;
ans = (ans + f[i][j][2]) % MX;
}
for (i = 1; i <= QAQ; i++)
ans = (ans + f[n][i][0]) % MX, ans = (ans + f[n][i][1]) % MX;
cout << ans << endl;
return 0;
}