排雷题解
简要题意
给定一个 的 01 大矩阵,询问 个 的 01 矩阵是否在大矩阵中出现过。
解析
本题是二维哈希的板子。
二维哈希其实就是二维前缀和与哈希的结合
我们先思考一维哈希实现过程:
在一维哈希中,我们是将长度为 的序列哈希为一个长度为 的一个 进制数(高位在前)。
类比一下,我们不难发现,二维哈希实际上就是由 (行数)个一维哈希哈希得到的一个 进制数(高位在前)。
显然有以下处理过程: 行哈希:首先对矩阵每一行的 个元素分别进行一维哈希。 列哈希:接着将矩阵的每一行视为一位,从上到下视作一个由 个元素组成的序列,类比一维哈希。
公式推导:求在一个大小为 的矩阵中大小为 的矩阵哈希值。
预先处理整个矩阵每一行的哈希值。设第 行第 列的元素为 ,则第 行前 列的哈希值:
再求 矩阵的哈希值。设右下角下标的为 的矩阵的哈希值为 。再用以上方法求出每一行的 个元素的哈希值。则其对应的第 行的 的矩阵的哈希 可以由该矩阵的所有元素向高位移高 位,再加上第 行的哈希值,再减去第 个矩阵的第 行的哈希值得到。即:
对于这道题,与 是一定的,所以就可以求出原矩阵中每个 的子矩阵的哈希值,将它们统计下来,然后二分一下即可(当然也可用 map
之类的标记一下)。
粘一下代码
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
const int maxn=1e3+5;
const int bs1=131,bs2=233;
int cnt;
ull hs[maxn][maxn],val[maxn*maxn],pw1[maxn],pw2[maxn];
char s[maxn];
int n,m,x,y,q;
int main() {
// ios::sync_with_stdio(0);
// cin.tie(0),cout.tie(0);
cin>>m>>n>>x>>y;
pw1[0]=pw2[0]=1;
for(int i=1; i<=n; i++)
pw1[i]=pw1[i-1]*bs1;
for(int i=1; i<=m; i++)
pw2[i]=pw2[i-1]*bs2;
for(int i=1; i<=m; i++) {
cin>>s+1;
for(int j=1; s[j]; j++)
hs[i][j]=hs[i][j-1]*bs1+s[j]-'0';
for(int j=1; s[j]; j++) {
hs[i][j] += hs[i-1][j]*bs2;
if(i>=x && j>=y)
val[++cnt]=hs[i][j]-hs[i-x][j]*pw2[x]-hs[i][j-y]*pw1[y]+hs[i-x][j-y]*pw2[x]*pw1[y];
}
}
sort(val+1,val+cnt+1);
cin>>q;
while(q--) {
ull h1=0,h2=0;
for(int i=1; i<=x; i++) {
cin>>s+1;
for(int j=1; s[j]; j++)
h2=h2*bs1+s[j]-'0';
h1=h1*bs2+h2,h2=0;
}
int pos=lower_bound(val+1,val+cnt+1,h1)-val;
if(val[pos]==h1)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}