P3290 SCOI2016围棋
轮廓线DP + KMP
用轮廓线 DP 算出无法匹配的个数, 用 3**(n*m) 减去即可
..#### f[x][y][s][i][j] 表示 s 为轮廓线, 模板第一行匹配到 i, 模板第二行匹配到 j
##?.?. s 第某位为 0/1 表示模板第一行是否能匹配到这一位
点击查看代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <array>
#include <queue>
using namespace std;
typedef long long LL;
constexpr int mod = 1e9 + 7;
int n, m, c, T;
char str[2][15]; // 模板串
int mb[2][15]; // str 变成的 01 串
int nxt[2][15]; // KMP nxt[][i] 表示 mb[1..t] = mb[i-t+1..i] 的最大 t (0≤t<i)
int tmp[2][15][3]; // KMP tmp[][i][color] 表示在 i 后面加上 color 后匹配到的位置
int f[2][1 << 10][15][15]; // [x][y] => [0/1]
inline LL qpow(LL b, LL p) {
LL res = 1;
while(p) {
if(p & 1) res = res * b % mod;
b = b * b % mod, p >>= 1;
}
return res;
}
// 取模
inline void add(int &x, const int &y) {
x += y;
if(x >= mod) x -= mod;
}
int main() {
scanf("%d%d%d%d", &n, &m, &c, &T);
int all = qpow(3, n * m);
while(T --) {
scanf("%s%s", str[0] + 1, str[1] + 1);
for(int t = 0; t < 2; t ++)
for(int i = 1; i <= c; i ++)
mb[t][i] = ((str[t][i] == 'X') ? 2 : (str[t][i] == 'B'));
// KMP
for(int t = 0; t < 2; t ++) {
mb[t][0] = mb[t][c + 1] = -1; // 边界条件
for(int i = 2, j = 0; i <= c; i ++) {
while(j && mb[t][i] != mb[t][j + 1]) j = nxt[t][j];
if(mb[t][i] == mb[t][j + 1]) j ++;
nxt[t][i] = j;
}
for(int i = 0; i <= c; i ++) {
for(int color = 0; color < 3; color ++) {
int now = i;
while(now && mb[t][now + 1] != color) now = nxt[t][now];
if(mb[t][now + 1] == color) now ++;
tmp[t][i][color] = now;
}
}
}
int full_set = 1 << (m - c + 1), now = 0;
memset(f, 0, sizeof(f));
f[0][0][0][0] = 1;
for(int x = 1; x <= n; x ++) {
for(int y = 1; y <= m; y ++) {
memset(f[!now], 0, sizeof(f[!now]));
for(int s = 0; s < full_set; s ++) {
for(int a = 0; a <= c; a ++) {
for(int b = 0; b <= c; b ++) {
for(int color = 0; color < 3; color ++) {
int nxts; // 新的 s
int nxta = tmp[0][a][color]; // 新的匹配的位置 a
int nxtb = tmp[1][b][color]; // 新的匹配的位置 b
if(y < c) nxts = s; // 一定不会变
else { // 是否匹配?
nxts = s | (1 << (y - c)); // 匹配到了
if(nxta != c) nxts ^= (1 << (y - c)); // 没有匹配到 -> 0
}
if(nxtb == c && (s >> (y - c) & 1)) continue; // 匹配到了 -> 不能转移
if(y == m) // 下一行的情况
add(f[!now][nxts][0][0], f[now][s][a][b]);
else
add(f[!now][nxts][nxta][nxtb], f[now][s][a][b]);
}
}
}
}
now ^= 1;
}
}
int res = 0;
for(int i = 0; i < full_set; i ++) add(res, f[now][i][0][0]);
printf("%d\n", (all - res + mod) % mod);
}
return 0;
}