排雷题解
简要题意
给定一个 \(m \times n\) 的 01 大矩阵,询问 \(q\) 个 $ x \times y$ 的 01 矩阵是否在大矩阵中出现过。
解析
本题是二维哈希的板子。
二维哈希其实就是二维前缀和与哈希的结合
我们先思考一维哈希实现过程:
在一维哈希中,我们是将长度为 \(m\) 的序列哈希为一个长度为 \(m\) 的一个 \(P\) 进制数(高位在前)。
类比一下,我们不难发现,二维哈希实际上就是由 \(n\)(行数)个一维哈希哈希得到的一个 \(n\) 进制数(高位在前)。
显然有以下处理过程:
行哈希:首先对矩阵每一行的 \(n\) 个元素分别进行一维哈希。
列哈希:接着将矩阵的每一行视为一位,从上到下视作一个由 \(n\)个元素组成的序列,类比一维哈希。
公式推导:求在一个大小为 $ n \times m$ 的矩阵中大小为 $ x \times y $ 的矩阵哈希值。
预先处理整个矩阵每一行的哈希值。设第 \(i\) 行第 \(j\) 列的元素为 \(s_{i,j}\),则第 \(i\) 行前 \(j\) 列的哈希值:$ h_{i,j}=h_{i,j-1}\times P+s_{i,j}$
再求 $ x \times y $ 矩阵的哈希值。设右下角下标的为 \((i,j)\)的$ x \times y $矩阵的哈希值为 $ H_{i,j}$ 。再用以上方法求出每一行的 \(y\) 个元素的哈希值。则其对应的第 \(i+1\) 行的 \(x \times y\) 的矩阵的哈希 \(H_{i+1,j}\) 可以由该矩阵的所有元素向高位移高 \(y\) 位,再加上第 \(i+1\) 行的哈希值,再减去第 \(i\) 个矩阵的第 \(1\) 行的哈希值得到。即:
\(H_{i+1,j}=H_{i,j}\times p^y+h_{i+1,(j-y+1,j)}-h_{i+1-x,(j-y+1,j)}\times p^{xy}\)
对于这道题,\(x\)与 \(y\) 是一定的,所以就可以求出原矩阵中每个 \(x\times y\) 的子矩阵的哈希值,将它们统计下来,然后二分一下即可(当然也可用 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;
}

浙公网安备 33010602011771号