二维Hash

前置知识:字符串hash

我们定义一个字符串s的hash值为:

\[\begin{aligned} \sum _ {i = 1} ^ n s[i] \times p1 ^ {(n-i)}\end{aligned} \]

其中n是字符串的长度,i从1~n(下标从1开始),p1是一个稍微大一点的质数

code:

	int p1=13331;
	unsigned long long h=0; 
	for(int i=1;i<=n;i++){
		h=h*p1+(s[i]-'a');
	} 

如果我们要知道字符串s区间[l,r]的hash值,我们要先求出s的每一个前缀字符串的hash值,令f[i]表示前i个字符组成的字符串的hash值,则

\(Hash([l,r])=f[r]-f[l-1] \times p ^ {(r-l+1)}\)

(可以把原字符串想象成一个p进制数来理解)

定义

  • 我们定义一个n行m列的矩阵s的hash值为

    $ \sum _{i=1}^n f[i] \times p2^{(n-i)}$

    f[i]是第i行的hash值,p2是一个不同于p1的质数

  • h[i][j]表示矩形s,前i行前j列组成的矩形的hash

于是可以得出求解h数组的代码:

void Hash1()
{
	for(int i=1;i<=n;i++)
	{
		unsigned long long tmp=0;
		for(int j=1;j<=m;j++)
		{
			tmp=tmp*p1+s[i][j]-'a';
			h[i][j]=tmp+h[i-1][j]*p2;
		}
	}
}

例题

acwing 矩阵

分析:

对于每一个询问,先处理出小矩阵的hash值,接着枚举大矩阵的每个点,如果能O(1)求出以这个点为右下角端点的大小为A\(\times\)B的矩阵的hash值,就可以O(1)判断了

那怎么求呢?
以n=6,m=8,i=5,j=6,a=2,b=3为例,下面每个格子代表一个字符,红色部分即为所求:(格子里的是那个字符的坐标)

\(h[i][j]-h[i-a][j] \times (p2)^a\)就得到了蓝色部分

我们再减掉黄色部分其实就可以得到答案了

那黄色部分怎么求呢?

黄色部分其实就是\(h[i][j-b]-h[i-a][j-b] \times (p2)^a\)

但是在蓝色部分-黄色部分的时候黄色部分整体要乘上\(p1^b\)

所以答案就是

\(h[i][j] - h[i-a][j] \times (p2)^a - ( h[i][j-b] - h[i-a][j-b] \times (p2)^a) \times (p1)^b\)

\(= h[i][j] - h[i-a][j] \times (p2)^a - h[i][j-b] \times (p1)^b + h[i-a][j-b] \times (p2)^a \times (p1)^b\)

其实和前缀和是一样的

最后附上完整代码

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
using namespace std;
const int N=1e5+5;
const unsigned long long p1=100003,p2=133331;
inline int read(){
    int w = 1, s = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; w *= (c == '-') ? -1 : 1, c = getchar());
    for (; c >= '0' && c <= '9'; s = 10 * s + (c - '0'), c = getchar());
    return s * w;
}
int n,m,a,b,q;
unsigned long long m1[1005],m2[1005];
char s[1005][1005]; 
unsigned long long h[1005][1005];
unsigned long long g[1005][1005];
void Hash1()
{
	for(int i=1;i<=n;i++)
	{
		unsigned long long tmp=0;
		for(int j=1;j<=m;j++)
		{
			tmp=tmp*p1+s[i][j]-'0';
			h[i][j]=tmp+h[i-1][j]*p2;
		}
	}
}
map<unsigned long long,bool> mp;
char t[1005][1005];
unsigned long long Hash2()
{
	unsigned long long hsh=0;
	for(int i=1;i<=a;i++)
	{
		unsigned long long tmp=0;
		for(int j=1;j<=b;j++)
		{
			tmp=tmp*p1+t[i][j]-'0';
		}
		hsh=hsh*p2+tmp;
	}
	return hsh;
}
signed main()
{
	m1[0]=m2[0]=1;
	for(int i=1;i<=1000;i++) m1[i]=m1[i-1]*p1,m2[i]=m2[i-1]*p2;
	n=read(),m=read(),a=read(),b=read();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>s[i][j];
		}
	}
	Hash1();
	for(int i=a;i<=n;i++)
	{
		for(int j=b;j<=m;j++)
		{
			g[i][j]=h[i][j]-h[i][j-b]*m1[b]-h[i-a][j]*m2[a]+h[i-a][j-b]*m1[b]*m2[a];
			mp[g[i][j]]=1; 
		}
	}
	q=read();
	while(q--)
	{
		for(int i=1;i<=a;i++)
		{
			for(int j=1;j<=b;j++)
			{
				cin>>t[i][j];
			}
		}
		unsigned long long hsh=Hash2();
		if(mp[hsh]) cout<<1<<endl;
		else cout<<0<<endl;
	}
	return 0;
}

posted @ 2024-09-02 16:48  Green&White  阅读(83)  评论(0编辑  收藏  举报