正题
考虑构造Dp:表示当前在第i行,第j列的轮廓线上,上一行匹配成功的结束端点集合为S,当前这行匹配k了模板第一行的k个,第二行的l个
考虑到集合S实际上只用记录有效的位置,也就是pos>=c,那么总状态数就是
转移枚举后一个位置放什么,用kmp来找第i个位置后面放一个x会转移到哪里,kmp的时间复杂度显然正确
若当前第二行新加进来一个字符满足第二行匹配c位,且S中第一位为1,那么这个状态就不合法,直接跳过.
否则也要更新S,将第一位弹掉,根据新的k来判断当前是否为一个合法的位置.
行转换的时候更新一下状态,使用滚动数组优化空间
总结
一开始居然看错题想了大半天,后来发现q个询问之间并没有关系,考试一定要看清楚题!
kmp,ACAM来维护匹配状态转移还是比较常见的,Dp这一块要找时间多做点题!
#include<bits/stdc++.h>
using namespace std;
int dp[110][13][1<<10][7][7];
int fail[2][10];
char s[2][10];
char w[3]={'W','B','X'};
int n,m,c,q,M,tot;
int mod=1e9+7;
void add(int&x,int y){x=x+y>=mod?x+y-mod:x+y;}
int ksm(int x,int t){
int tot=1;
while(t){
if(t&1) tot=1ll*tot*x%mod;
x=1ll*x*x%mod;
t/=2;
}
return tot;
}
void solve(){
for(int k=0;k<2;k++){
scanf("%s",s[k]+1);
for(int i=1;i<=c;i++){
int now=fail[k][i-1];
while(now!=-1 && s[k][now+1]!=s[k][i]) now=fail[k][now];
fail[k][i]=now+1;
}
}
int now=0;
memset(dp[now],0,sizeof(dp[now]));
dp[now][0][0][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++)
for(int S=0;S<M;S++)
for(int k=0;k<=c;k++)
for(int l=0;l<=c;l++) if(dp[now][j][S][k][l])
for(int x=0;x<3;x++){
int nk=k,nl=l,nS=S;
while(nk!=-1 && s[0][nk+1]!=w[x]) nk=fail[0][nk];nk++;
while(nl!=-1 && s[1][nl+1]!=w[x]) nl=fail[1][nl];nl++;
if((S&1) && nl==c) continue;
if(j+1>=c){
nS>>=1;
nS|=(nk==c)*(M>>1);
}
add(dp[now][j+1][nS][nk][nl],dp[now][j][S][k][l]);
}
memset(dp[now^1],0,sizeof(dp[now^1]));
for(int S=0;S<M;S++){
for(int k=0;k<=c;k++)
for(int l=0;l<=c;l++)
add(dp[now^1][0][S][0][0],dp[now][m][S][k][l]);
}
now^=1;
}
int ans=tot;
for(int S=0;S<M;S++) add(ans,mod-dp[now][0][S][0][0]);
printf("%d\n",ans);
}
int main(){
scanf("%d %d %d %d",&n,&m,&c,&q);M=(1<<(m-c+1));tot=ksm(3,n*m);
fail[0][0]=fail[1][0]=-1;
while(q--) solve();
}

本文探讨了在给定的二维网格上进行模板匹配的问题,利用动态规划(DP)和Knuth-Morris-Pratt算法(KMP)进行高效的状态转移,通过滚动数组优化空间复杂度。文章详细介绍了算法实现过程,包括状态定义、状态转移方程及代码实现。
567

被折叠的 条评论
为什么被折叠?



