洛谷传送门
BZOJ传送门
LOJ传送门
解析:
由于空间存的下并没有用哈希表优化状态转移,不然还能快一点,主要是哈希表的存参后的解码会稍微麻烦一些。
至少匹配一次,这个至少并不好处理,发现总的方案数始终是 3 n m 3^{nm} 3nm,所以我们考虑计算一次都没有匹配的方案数。
对于上一行的状态,我们只关心它有没有匹配完第一行,直接用一个二进制数压一下就行了。
在第一行匹配上的情况下如果当前行匹配上了第二行就要舍弃掉。
明白这些之后我们来定义状态,设 f [ i ] [ j ] [ s ] [ a ] [ b ] f[i][j][s][a][b] f[i][j][s][a][b]表示处理到第 i i i行第 j j j个格子,轮廓线状态为 s s s,当前行匹配模式矩阵第一行可以匹配到 a a a,匹配第二行可以匹配到 b b b,并且之前与模式矩阵没有发生一次匹配的总方案数。
这道题轮廓线定义稍微有点麻烦,假设当前处理到当前行第 j j j个格子(j>c),那么 0 − ( j − c − 1 ) 0-(j-c-1) 0−(j−c−1)的位置上都是表示这一行的位置能否和模式矩阵第一行匹配,而 j − c j-c j−c位以及之后的才表示上一行的状态。
明白了这点之后状态转移也就十分方便了。
不过为了知道添加字符后匹配位置的变化,我们需要先对每一个串单独建立一个AC自动机(Trie图),可以直接利用KMP的nxt数组来加速构建。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cin;
using std::cout;
using std::cerr;
cs int mod=1e9+7;
inline void Inc(int &a,int b){((a+=b)>=mod)?a-=mod:a;}
inline int quickpow(int a,int b,int res=1){
while(b){
if(b&1)res=(ll)res*a%mod;
a=(ll)a*a%mod;
b>>=1;
}
return res;
}
int n,m,c,q,tot,mask;
int f[2][1<<12][7][7],now;
char s1[7],s2[7];
int a1[7],a2[7];
int nxt1[7],nxt2[7];
int tr1[7][3],tr2[7][3];
inline int id(char c){
switch(c){
case 'W':return 0;
case 'B':return 1;
case 'X':return 2;
}
}
inline void init(){
cin>>(s1+1)>>(s2+1);
for(int re i=1;i<=c;++i)a1[i]=id(s1[i]),a2[i]=id(s2[i]);
for(int re i=2,j=0;i<=c;++i){
while(j&&a1[j+1]!=a1[i])j=nxt1[j];
if(a1[j+1]==a1[i])++j;
nxt1[i]=j;
}
for(int re i=2,j=0;i<=c;++i){
while(j&&a2[j+1]!=a2[i])j=nxt2[j];
if(a2[j+1]==a2[i])++j;
nxt2[i]=j;
}
for(int re i=0;i<c;++i)for(int re j=0;j<3;++j){
int re k=i;
while(k&&a1[k+1]!=j)k=nxt1[k];
if(a1[k+1]==j)++k;
tr1[i][j]=k;
}
for(int re i=0;i<c;++i)for(int re j=0;j<3;++j){
int re k=i;
while(k&&a2[k+1]!=j)k=nxt2[k];
if(a2[k+1]==j)++k;
tr2[i][j]=k;
}
memset(f[0],0,sizeof f[0]);
f[0][0][0][0]=1;now=1;
}
#define pre now^1
inline void solve(){
init();
for(int re i=1;i<=n;++i){
memset(f[now],0,sizeof f[now]);
for(int re s=0;s<mask;++s)
for(int re i=0;i<c;++i)
for(int re j=0;j<c;++j)
Inc(f[now][s][0][0],f[pre][s][i][j]);
now=pre;
for(int re j=1;j<=m;++j){
memset(f[now],0,sizeof f[now]);
for(int re s=0;s<mask;++s)
for(int re a=0;a<c;++a)
for(int re b=0;b<c;++b)if(f[pre][s][a][b]){
for(int re k=0;k<3;++k){
int pa=tr1[a][k],pb=tr2[b][k],S=s;
if(j>=c&&(S&(1<<j-c)))S^=1<<j-c;
if(pa==c)S^=1<<j-c,pa=nxt1[c];
if(pb==c)if(s&(1<<j-c))continue;
else pb=nxt2[c];
Inc(f[now][S][pa][pb],f[pre][s][a][b]);
}
}
now=pre;
}
}
int ans=tot;
for(int re i=0;i<mask;++i)
for(int re a=0;a<c;++a)
for(int re b=0;b<c;++b)
ans=(ans-f[pre][i][a][b]+mod)%mod;
cout<<ans<<"\n";
}
signed main(){
std::ios::sync_with_stdio(false);
cin>>n>>m>>c>>q;
mask=1<<(m-c+1),tot=quickpow(3,n*m);
while(q--)solve();
return 0;
}