题目描述
近日,谷歌研发的围棋 AI——AlphaGo 以 4:14:1 的比分战胜了曾经的世界冠军李世石,这是人工智能领域的又一里程碑。
与传统的搜索式 AI 不同,AlphaGo 使用了最近十分流行的卷积神经网络模型。在卷积神经网络模型中,棋盘上每一块特定大小的区域都被当做一个窗口。例如棋盘的大小为 5\times 65×6,窗口大小为 2\times 42×4,那么棋盘中共有 1212 个窗口。此外,模型中预先设定了一些模板,模板的大小与窗口的大小是一样的。
下图展现了一个 5\times 65×6 的棋盘和两个 2\times 42×4 的模板:
对于一个模板,只要棋盘中有某个窗口与其完全匹配,我们称这个模板是被激活的,否则称这个模板没有被激活。
例如图中第一个模板就是被激活的,而第二个模板就是没有被激活的。我们要研究的问题是:对于给定的模板,有多少个棋盘可以激活它。
为了简化问题,我们抛开所有围棋的基本规则,只考虑一个 n\times mn×m 的棋盘,每个位置只能是黑子、白子或无子三种情况,换句话说,这样的棋盘共有 3^{n\times m}3
n×m
种。此外,我们会给出 qq 个 2\times c2×c 的模板。
我们希望知道,对于每个模板,有多少种棋盘可以激活它。强调:模板一定是两行的。
输入格式
输入数据的第一行包含四个正整数 n,m,cn,m,c 和 qq,分别表示棋盘的行数、列数、模板的列数和模板的数量。
随后 2\times q2×q 行,每连续两行描述一个模板。其中,每行包含 cc 个字符,字符一定是 W,B 或 X 中的一个,表示白子、黑子或无子三种情况的一种。
输出格式
输出应包含 qq 行,每行一个整数,表示符合要求的棋盘数量。由于答案可能很大,你只需要输出答案对 10^9+710
9
+7 取模后的结果即可。
输入输出样例
输入 #1复制
3 1 1 2
B
W
B
B
输出 #1复制
6
5
说明/提示
对于所有测试点:1\leq n\leq 1001≤n≤100,1\leq m\leq 121≤m≤12,1\leq c\leq 61≤c≤6,1\leq q\leq 51≤q≤5。
测试点编号 约定
11 n=3n=3,m=4m=4,c=2c=2
22 n=4n=4,m=4m=4,c=3c=3
33 n=2n=2,m=9m=9,c=6c=6
44 n=2n=2,m=12m=12,c=3c=3
55 n=2n=2,m=12m=12,c=5c=5
66 n=10n=10,m=8m=8,c=3c=3
77 n=10n=10,m=10m=10,c=5c=5
88 n=100n=100,m=10m=10,c=5c=5
99 n=100n=100,m=12m=12,c=5c=5
1010 n=100n=100,m=12m=12,c=6c=6
做这道题目时马上就想到了轮廓线DP,“至少有一次匹配”可以转化为“禁止出现匹配”。
设dp[i][j][mask][k][l]考虑到了i行j列这个位置,而mask是一个状压,
表示当前轮廓线有那些位置可以和第一行完全匹配。
但是1…c−1这些列显然不可能完全匹配,
所以maskmask只需要记录m-c+1m−c+1个二进制位。
kk表示第ii行1\dots j1…j位最多能匹配到模板串第一行的哪个位置,
ll表示第ii行1\dots j1…j位最多能匹配到模板串第二行的哪个位置。
显然,当l=cl=c且maskmask的第一个二进制位(也就是代表了i,ji,j正上方的位置)为11时,
就会出现一次和模板串的完整匹配,因此这种转移是不合法的。
否则,其他情况下我们都可以转移。
新的kk, ll可以用KMP求出。
新的maskmask相比原来的maskmask要去掉第一位,然后新加入一位:若k=ck=c则新加入的位为11,否则为00.
DP数组的前两维可以滚动使用,这样优化了空间
在dp完后要把结果累加到dp[i][j][mask][0][0]dp[i][j][mask][0][0]上:因为下一行又要重新开始匹配了。
时间复杂度
***O(nm⋅2 m−c+1 ⋅c 2)。***
代码
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int MOD=1e9+7;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int& x,int y){x=mod1(x+y);}
inline void sub(int& x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
const int MAXN=100,MAXM=12;
const char biao[3]={'W','B','X'};
int n,m,c,q,nxt[2][MAXM+5],dp[2][1<<MAXM][7][7];
char s[2][MAXM+5];
int main() {
cin>>n>>m>>c>>q;
int totways=pow_mod(3,n*m);
while(q--){
//memset(nxt,0,sizeof(nxt));
for(int t=0;t<=1;++t){
cin>>(s[t]+1);
nxt[t][1]=0;
for(int i=2,j=0;i<=c;++i){
while(j&&s[t][j+1]!=s[t][i])j=nxt[t][j];
if(s[t][j+1]==s[t][i])++j;
nxt[t][i]=j;
}
}//kmp
memset(dp,0,sizeof(dp));
dp[0][0][0][0]=1;
int len=m-c+1;
int M=1<<len;
int cur=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cur^=1;
memset(dp[cur],0,sizeof(dp[cur]));
for(int mask=0;mask<M;++mask){
for(int k=0;k<=c;++k){
for(int l=0;l<=c;++l)if(dp[cur^1][mask][k][l]){
for(int x=0;x<=2;++x){
int nk=k;
while(nk&&(nk==c||s[0][nk+1]!=biao[x]))nk=nxt[0][nk];
if(s[0][nk+1]==biao[x])nk++;
int nl=l;
while(nl&&(nl==c||s[1][nl+1]!=biao[x]))nl=nxt[1][nl];
if(s[1][nl+1]==biao[x])nl++;
if(j>=c&&nl==c&&(mask&1))continue;
int newmask=mask;
if(j>=c){
newmask>>=1;
newmask|=(nk==c)<<(len-1);
}
add(dp[cur][newmask][nk][nl],dp[cur^1][mask][k][l]);
}
}
}
}
}
for(int mask=0;mask<M;++mask){
for(int j=0;j<=c;++j){
for(int k=0;k<=c;++k)if(j||k){
add(dp[cur][mask][0][0],dp[cur][mask][j][k]);
dp[cur][mask][j][k]=0;
}
}
}
}
int ans=0;
for(int mask=0;mask<M;++mask){
add(ans,dp[cur][mask][0][0]);
}
ans=mod2(totways-ans);
cout<<ans<<endl;
}
return 0;
}