题意简化,给你一个大矩阵和一个小矩阵,求小矩阵在大矩阵中出现了多少次,矩阵都不得旋转
这个题就是字符串匹配的二维版,然而不像数据结构,它的二维版并不复杂只是一行行拆开处理,但复杂度十分优越,几乎等于读入时间(vjudge上跑了0ms)
首先将小矩阵的每一行分开看,这样小矩阵就成了多个模板串,建个AC自动机。再用大矩阵的每一行去AC自动机里找匹配,例如大矩阵第i行的第j位与第k个模板串(小矩阵第k行)匹配上了,那么(i-(k-1),j)就是一个满足一行相等的右上角。开个C数组保存一个点作为右上角的小矩阵与目标小矩阵有几行一样,这样C[i][j]=x的点就是合法点。统计一下合法点的个数,即为答案
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define MAXN (1010)
using namespace std;
int A[MAXN][MAXN],B[MAXN][MAXN],n,m,x,y,son[MAXN*MAXN][151];
int Size,C[MAXN][MAXN],f[MAXN*MAXN],h[MAXN*MAXN],m1;
char S[MAXN];
struct edge{
int next,to;
void Add(int Next,int To){
next=Next; to=To;
}
}q[MAXN*MAXN];
void addedge(int x,int y){
q[++m1].Add(h[x],y); h[x]=m1;
}
void Build(int Num){
int rt=0,i,d;
for (i=1;i<=y;i++){
int d=B[Num][i];
if (!son[rt][d]) son[rt][d]=++Size;
rt=son[rt][d];
}
addedge(rt,Num);
}
void Get_fail(){
queue <int> Q;
memset(f,0,sizeof(f));
int i;
for (i=0;i<150;i++)
if (son[0][i])
Q.push(son[0][i]);
while (!Q.empty()){
int x=Q.front(); Q.pop();
for (i=0;i<=150;i++){
if (!son[x][i]){
son[x][i]=son[f[x]][i];
continue;
}
Q.push(son[x][i]);
int v=f[x],u=son[x][i];
while (v&&!son[v][i]) v=f[v];
f[u]=son[v][i];
}
}
}
void Find(int Num){
int i,rt=0,j,y;
for (i=1;i<=m;i++){
int d=A[Num][i];
rt=son[rt][d];
for (j=h[rt];j;j=q[j].next){
y=q[j].to;
C[Num-(y-1)][i]++;
}
}
}
void Work(){
scanf("%d %d",&n,&m);
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
memset(son,0,sizeof(son));
memset(h,0,sizeof(h));
memset(C,0,sizeof(C));
memset(q,0,sizeof(q));
Size=0; m1=0;
int i,j;
for (i=1;i<=n;i++){
scanf("%s",S);
for (j=1;j<=m;j++)
A[i][j]=S[j-1];
}
scanf("%d %d",&x,&y);
for (i=1;i<=x;i++){
scanf("%s",S);
for (j=1;j<=y;j++)
B[i][j]=S[j-1];
Build(i);
}
Get_fail();
for (i=1;i<=n;i++)
Find(i);
int ans=0;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (C[i][j]==x) ans++;
printf("%d\n",ans);
}
int main(){
int T; cin>>T;
while (T--) Work();
}