LA 6932 Vocabulary

Description

有三个字符串\(A,B,C\),他们都由小写字母和问号组成,其中问号可以代表任意小写字母。

已知在字典序上有\(A<B<C\)(这里的小于是严格小于),求有多少种问号填充能满足条件。

多组数据,\(1\leq |A|,|B|,|C|\leq 10^6\)

Solution

先考虑一个朴素的DP……\(d_{i, 0/1, 0/1}\)表示考虑前\(i\)位的话目前\(A\)是等于/小于\(B\)\(B\)是等于/小于\(C\)的方案数。转移的话考虑刷表,枚举三个串下一位都是啥,转移就行了。但是这个东西复杂度是\(26^3n\)的……会炸掉,考虑优化。

转移很麻烦,我们考虑来预处理那个转移……\(f_{i, j, k, 0/1/2, 0/1/2}\)表示三个字符\(i, j, k\)(可能有\(\text{?}\)或者\(\epsilon\)),要求\(i\)\(j\)的关系是小于/等于/大于,\(j\)\(k\)的关系是小于/等于/大于的方案数,这个东西随便暴力一下就能高效的预处理出来。这样转移就很快力,,,

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <utility>
using ll = long long;
const ll ha = 1000000009;
template<typename T>
int cmp(T a, T b) {
if(a < b) {
return 0;
} else if(a == b) {
return 1;
} else {
return 2;
}
}

int f[28][28][28][4][4];
void process() {
for(int i = 0; i <= 27; i ++) {
for(int j = 0; j <= 27; j ++) {
for(int k = 0; k <= 27; k ++) {
int l1 = i, r1 = i;
if(i == 27) l1 = 1, r1 = 26;
for(int a = l1; a <= r1; a ++) {
int l2 = j, r2 = j;
if(j == 27) l2 = 1, r2 = 26;
for(int b = l2; b <= r2; b ++) {
int l3 = k, r3 = k;
if(k == 27) l3 = 1, r3 = 26;
for(int c = l3; c <= r3; c ++) {
f[i][j][k][cmp(a, b)][cmp(b, c)] ++;
}
}
}
}
}
}
for(int i = 0; i <= 27; i ++) {
for(int j = 0; j <= 27; j ++) {
for(int k = 0; k <= 27; k ++) {
for(int a = 0; a < 3; a ++) {
for(int b = 0; b < 3; b ++) {
f[i][j][k][a][3] = (f[i][j][k][a][3] + f[i][j][k][a][b]) % ha;
f[i][j][k][3][b] = (f[i][j][k][3][b] + f[i][j][k][a][b]) % ha;
f[i][j][k][3][3] = (f[i][j][k][3][3] + f[i][j][k][a][b]) % ha;
}
}
}
}
}
}

int idx(char c) {
if(c == '?') {
return 27;
} else if((int)c == 0) {
return 0;
} else {
return c - 'a' + 1;
}
}

const int maxn = 1000005;
char S1[maxn], S2[maxn], S3[maxn];
ll d[maxn][2][2];
int main() {
process();
int T; scanf("%d", &T);
while(T --) {
memset(S1, 0, sizeof(S1));
memset(S2, 0, sizeof(S2));
memset(S3, 0, sizeof(S3));
scanf("%s%s%s", S1 + 1, S2 + 1, S3 + 1);
int n1 = strlen(S1 + 1), n2 = strlen(S2 + 1), n3 = strlen(S3 + 1);
d[0][0][0] = 1;
int n = std::max(n1, std::max(n2, n3));
for(int i = 0; i < n; i ++) {
memset(d[i + 1], 0, sizeof(d[i + 1]));
int a = idx(S1[i + 1]), b = idx(S2[i + 1]), c = idx(S3[i + 1]);
d[i + 1][1][1] += d[i][1][1] * (ll)f[a][b][c][3][3] % ha;
if(d[i + 1][1][1] >= ha) d[i + 1][1][1] -= ha;
d[i + 1][0][1] += d[i][0][1] * (ll)f[a][b][c][1][3] % ha;
if(d[i + 1][0][1] >= ha) d[i + 1][0][1] -= ha;
d[i + 1][1][1] += d[i][0][1] * (ll)f[a][b][c][0][3] % ha;
if(d[i + 1][1][1] >= ha) d[i + 1][1][1] -= ha;
d[i + 1][1][0] += d[i][1][0] * (ll)f[a][b][c][3][1] % ha;
if(d[i + 1][1][0] >= ha) d[i + 1][1][0] -= ha;
d[i + 1][1][1] += d[i][1][0] * (ll)f[a][b][c][3][0] % ha;
if(d[i + 1][1][1] >= ha) d[i + 1][1][1] -= ha;
for(int j = 0; j < 2; j ++) {
for(int k = 0; k < 2; k ++) {
d[i + 1][1 - j][1 - k] += d[i][0][0] * (ll)f[a][b][c][j][k] % ha;
if(d[i + 1][1 - j][1 - k] >= ha) d[i + 1][1 - j][1 - k] -= ha;
}
}
}
printf("%lld\n", d[n][1][1]);
}
return 0;
}