排雷题解

简要题意

给定一个 m×nm \times n 的 01 大矩阵,询问 qqx×y x \times y 的 01 矩阵是否在大矩阵中出现过。

解析

本题是二维哈希的板子。

二维哈希其实就是二维前缀和与哈希的结合

我们先思考一维哈希实现过程:

在一维哈希中,我们是将长度为 mm 的序列哈希为一个长度为 mm 的一个 PP 进制数(高位在前)。

类比一下,我们不难发现,二维哈希实际上就是由 nn(行数)个一维哈希哈希得到的一个 nn 进制数(高位在前)。

显然有以下处理过程: 行哈希:首先对矩阵每一行的 nn 个元素分别进行一维哈希。 列哈希:接着将矩阵的每一行视为一位,从上到下视作一个由 nn个元素组成的序列,类比一维哈希。

公式推导:求在一个大小为 n×m n \times m 的矩阵中大小为 x×yx \times y 的矩阵哈希值。

预先处理整个矩阵每一行的哈希值。设第 ii 行第 jj 列的元素为 si,js_{i,j},则第 ii 行前 jj 列的哈希值:hi,j=hi,j1×P+si,j h_{i,j}=h_{i,j-1}\times P+s_{i,j}

再求 x×yx \times y 矩阵的哈希值。设右下角下标的为 (i,j)(i,j)x×yx \times y矩阵的哈希值为 Hi,j H_{i,j} 。再用以上方法求出每一行的 yy 个元素的哈希值。则其对应的第 i+1i+1 行的 x×yx \times y 的矩阵的哈希 Hi+1,jH_{i+1,j} 可以由该矩阵的所有元素向高位移高 yy 位,再加上第 i+1i+1 行的哈希值,再减去第 ii 个矩阵的第 11 行的哈希值得到。即: Hi+1,j=Hi,j×py+hi+1,(jy+1,j)hi+1x,(jy+1,j)×pxyH_{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}

对于这道题,xxyy 是一定的,所以就可以求出原矩阵中每个 x×yx\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  阅读(0)  评论(0编辑  收藏  举报  来源