题意简化,给你一个大矩阵和一个小矩阵,求小矩阵在大矩阵中出现了多少次,矩阵都不得旋转
这个题就是字符串匹配的二维版,然而不像数据结构,它的二维版并不复杂只是一行行拆开处理,但复杂度十分优越,几乎等于读入时间(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(); }