状压DP。定义状态
f[i][S]
f
[
i
]
[
S
]
表示前
i
i
位,能匹配的字符串集合为的方案数。
先预处理出
g[i][c]
g
[
i
]
[
c
]
,表示在第
i
i
位选用字符,能匹配的字符串集合。
从
i
i
转移到,即枚举第
i+1
i
+
1
个字符:
f[i+1][S∩g[i+1][c]]+=f[i][j]
f
[
i
+
1
]
[
S
∩
g
[
i
+
1
]
[
c
]
]
+
=
f
[
i
]
[
j
]
最后答案就是
∑|S|=Kf[m][S]
∑
|
S
|
=
K
f
[
m
]
[
S
]
。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int ZZQ = 1e6 + 3, N = 55, C = (1 << 15) + 5, L = 30, E = 20;
int n, K, m, Cm, com[N][L], f[N][C];
char s[E][N];
void work() {
int i, j, k; scanf("%d%d", &n, &K); Cm = 1 << n;
for (i = 1; i <= n; i++) scanf("%s", s[i] + 1);
m = strlen(s[1] + 1); for (i = 0; i <= m; i++) {
for (j = 0; j < 26; j++) com[i][j] = 0;
for (j = 0; j < Cm; j++) f[i][j] = 0;
}
for (i = 1; i <= m; i++) for (j = 0; j < 26; j++) for (k = 1; k <= n; k++)
if (s[k][i] == j + 'a' || s[k][i] == '?')
com[i][j] = com[i][j] | (1 << k - 1); f[0][Cm - 1] = 1;
for (i = 0; i < m; i++) for (j = 0; j < Cm; j++) if (f[i][j])
for (k = 0; k < 26; k++)
(f[i + 1][j & com[i + 1][k]] += f[i][j]) %= ZZQ;
int ans = 0; for (j = 0; j < Cm; j++) {
int cnt = 0; for (i = 0; i < n; i++) cnt += (j >> i) & 1;
if (cnt == K) ans = (ans + f[m][j]) % ZZQ;
}
printf("%d\n", ans);
}
int main() {
int T; cin >> T;
while (T--) work();
return 0;
}