给定一个M行N列的01矩阵(只包含数字0或1的矩阵),再执行Q次询问,每次询问给出一个A行B列的01矩阵,求该矩阵是否在原矩阵中出现过。

输入格式

第一行四个整数M,N,A,B。

接下来一个M行N列的01矩阵,数字之间没有空格。

接下来一个整数Q。

接下来Q个A行B列的01矩阵,数字之间没有空格。

输出格式

对于每个询问,输出1表示出现过,0表示没有出现过。

数据范围

A100A≤100,M,N,B1000M,N,B≤1000,Q1000Q≤1000

输入样例:

3 3 2 2
111
000
111
3
11
00
11
11
00
11

输出样例:

1
0
1

题意:判断查询时输入的矩阵是否是上面那个的子矩阵
思路:哈希可以用快速判断是否相等,我们可以预处理出上面大矩阵的所有小矩阵的哈希值,我们每个位置再去遍历肯定不行那样就是(n*m)^2,会超时,我们可以利用哈希的加减性质,我们首先计算出每一行的前缀哈希值
然后我们再预处理子矩阵的时候利用哈希值合并的性质只用遍历每一行,这样就省去了一个m,不会超时,然后用map保留下来所有的值,后面查询时候也计算每个矩阵的哈希值即可,判断map中是否存在
因为我们的哈希只能记录一维的哈希值,二维的记录会极大可能发生冲突,只能用二维转化成一维再去记录,切记要用unsigned long long ,可能很多人会问为什么不用mod,因为ull自带mod功能
#include<bits/stdc++.h>
#define maxn 1005
#define mod 1000000007
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll m,n,a,b;
char str[maxn][maxn],s[maxn][maxn];
ull dp[maxn][maxn];
ull f[maxn];
map<ull,ll> mp; 
void hash_code(){//记录所有行的前缀哈希值
    for(int i=1;i<=m;i++){
        f[0]=1;
        for(int j=1;j<=n;j++){
            dp[i][j]=dp[i][j-1]*131+str[i][j]-'0'+1; 
            if(i==1) f[j]=f[j-1]*131;
        }
    }
}
void init(){
    for(int i=1;i<=m-a+1;i++){
        for(int j=1;j<=n-b+1;j++){
            ull sum=0;
            for(int k=i;k<i+a;k++){//利用哈希合并性质省去一层循环
                sum=sum*f[b]+dp[k][j+b-1]-dp[k][j-1]*f[b];
            }
            mp[sum]=1;
            //printf("%llu\n",sum);
        }
    }
}
int main(){
    scanf("%lld%lld%lld%lld",&m,&n,&a,&b);
    for(int i=1;i<=m;i++){
        scanf("%s",str[i]+1);
    }
    hash_code();
    init();
    ll q;
    scanf("%lld",&q);
    for(int i=0;i<q;i++){
        for(int j=1;j<=a;j++){
            scanf("%s",s[j]+1);
        }
        ull sum=0;
        for(int j=1;j<=a;j++){
            for(int k=1;k<=b;k++){
                sum=sum*131+s[j][k]-'0'+1;
            }
        }
        //printf("%llu\n",sum);
        if(mp[sum]) printf("1\n");
        else printf("0\n");
    } 
}