二维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;
}
}
}
例题
分析:
对于每一个询问,先处理出小矩阵的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;
}