题意
给定一个 (n imes m) 的矩阵,在给定一个 (x imes y) 的小矩阵,求小矩阵在大矩阵中出现的次数。
(1 leq n,m leq 1000)
(1leq x,y leq 100)
思路
做法比较显然,先对大矩阵哈希,在每个位上确定一个“位权”,(Base^k) ,对于矩阵的 ((x,y)) 位置,可以令 (k=(x-1)*m+y-1) ,然后求二维前缀和。接下来把小矩阵放在大矩阵的 ((1,1)(x,y)) 位置哈希,将哈希值进行比较。接下来考虑的就是移动矩阵哈希值的变化,不难发现,因为 ((1,1)) 的位权是 (1) ,所以移动到哪里,哈希值就乘上那里的位权即可。
关于哈希的基数和模数的取值,首先基数 (Base) 要大于不同元素的个数,模数尽量取大质数,最好是孪生的,但注意 (1e9+7,1e9+9) 由于太大众,不免会被卡,我个人习惯再取一个 (19260817) 。然后 (101111,101113,101117,101119) 也是挺好记的质数,在用链式哈希表的时候可以当一维数组下标。
代码
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int Base[3]={29,31,37};
const int Mod[3]={(int)1e9+7,(int)1e9+9,1926081};
const int N=1005;
char A[N][N],B[N][N];
LL pB[3][N*N];
LL s[3][N][N],h[3];
int n,m,p,q;
inline int Hs(int x,int y){return (x-1)*m+y-1;}
LL S(int k,int X1,int Y1,int X2,int Y2)
{
return (
(s[k][X2][Y2]-s[k][X1-1][Y2]-s[k][X2][Y1-1]+s[k][X1-1][Y1-1])
%Mod[k]+Mod[k]
)%Mod[k];
}
int main()
{
FOR(k,0,2){pB[k][0]=1;FOR(i,1,N*N-1)pB[k][i]=pB[k][i-1]*Base[k]%Mod[k];}
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%s",A[i]+1);
scanf("%d%d",&p,&q);
FOR(i,1,p)scanf("%s",B[i]+1);
if(p>n||q>m){puts("0");continue;}
FOR(k,0,2)FOR(i,1,n)FOR(j,1,m)
s[k][i][j]=(
(s[k][i][j-1]+s[k][i-1][j]-s[k][i-1][j-1]+(A[i][j]-'a'+1)*pB[k][Hs(i,j)])
%Mod[k]+Mod[k]
)%Mod[k];
FOR(k,0,2)
{
h[k]=0;
FOR(i,1,p)FOR(j,1,q)h[k]=(h[k]+(B[i][j]-'a'+1)*pB[k][Hs(i,j)])%Mod[k];
}
int cnt=0;
FOR(i,1,n-p+1)FOR(j,1,n-q+1)
{
bool flag=1;
FOR(k,0,2)if(
h[k]*pB[k][Hs(i,j)]%Mod[k]!=S(k,i,j,i+p-1,j+q-1)
)flag=0;
if(flag)cnt++;
}
printf("%d
",cnt);
}
return 0;
}