排雷题解

简要题意

给定一个 \(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;
}
posted @ 2024-08-09 15:00  p7gab  阅读(37)  评论(0)    收藏  举报  来源