题目:BZOJ2351.
题目大意:给定一个
N
∗
M
N*M
N∗M的大
01
01
01矩阵,以及
Q
Q
Q个
A
B
AB
AB的小
01
01
01矩阵,问有每个小矩阵是否在大矩阵中出现.
1
≤
n
,
m
≤
1
0
3
,
q
=
1
0
3
1\leq n,m\leq 10^3,q=10^3
1≤n,m≤103,q=103,小矩阵为边长
≤
100
\leq 100
≤100的正方形.
看到这道题我们就会想到考虑这个问题在一维情况下的做法,直接什么AC自动机,什么KMP之类的算法都可以解决.
但是考虑在二维情况下怎么办?二维AC自动机 矩阵hash!
我们考虑字符串hash是把一个字符串看成一个 P P P进制数,那么矩阵hash呢?考虑先把矩阵的每一行看成一个 P 1 P1 P1进制数得到所有数,再把每一行所表示的 P P P进制数看成一个数,把整个矩阵看成一个字符串,那么再把这个字符串当成一个 P 2 P2 P2进制数就可以了.
具体来说,我们设置一个数 P 1 P1 P1,先把每一行当成一个字符串预处理出每个前缀对应的 P 1 P1 P1进制数.然后对于每一个 i i i行 j j j列的前缀矩阵,我们只要把每一行 j j j个数的前缀对应的 P 1 P1 P1进制数当成这个子矩阵对应的一个字符串,然后把这个字符串当成一个 P 2 P2 P2进制数预处理一下每个前缀对应的 P 2 P2 P2进制数即可.
那么在这个情况下,我们类比二维前缀和,设
i
i
i行
j
j
j列的前缀矩阵对应的值为
s
[
1..
i
]
[
1..
j
]
s[1..i][1..j]
s[1..i][1..j],第
x
1
+
1
x1+1
x1+1行到第
x
2
x2
x2行,第
y
1
+
1
y1+1
y1+1列到
y
2
y2
y2列所对应的数值为
c
(
x
1
,
x
2
,
y
1
,
y
2
)
c(x1,x2,y1,y2)
c(x1,x2,y1,y2),那么:
c
(
x
1
,
x
2
,
y
1
,
y
2
)
=
s
[
x
2
]
[
y
2
]
−
P
2
y
2
−
y
1
s
[
x
2
]
[
y
1
]
−
P
1
x
2
−
x
1
s
[
x
1
]
[
y
2
]
+
P
2
y
2
−
y
1
P
1
x
2
−
x
1
s
[
x
1
]
[
y
1
]
c(x1,x2,y1,y2)=s[x2][y2]-P2^{y2-y1}s[x2][y1]-P1^{x2-x1}s[x1][y2] +P2^{y2-y1}P1^{x2-x1}s[x1][y1]
c(x1,x2,y1,y2)=s[x2][y2]−P2y2−y1s[x2][y1]−P1x2−x1s[x1][y2]+P2y2−y1P1x2−x1s[x1][y1]
这个大概还好理解吧…我知道写得很丑
反正理解了这个式子后,我们就可以直接写出代码了(注:这道题有一道相同的题BZOJ2462,但是这份代码在那道题上会TLE,大概是被卡常或者卡hash了):
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
typedef unsigned long long ULL;
const int N=1000;
const ULL P1=131,P2=13331,M=5000081;
char rc(){
char c=getchar();
while (c<'0'||c>'1') c=getchar();
return c;
}
ULL s[N+9][N+9],Pow1[N+9],Pow2[N+9];
int n,m,a,b;
ULL Hash(int a,int b,int c,int d){
return s[b][d]-s[b][c-1]*Pow1[d-c+1]-s[a-1][d]*Pow2[b-a+1]+s[a-1][c-1]*Pow2[b-a+1]*Pow1[d-c+1];
}
struct Hash_table{
int h[M+9];
ULL v[M+9];
void add(ULL &x,const ULL &b){x+=b;if (x>=M) x-=M;}
void insert(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
++h[x];v[x]=t;
}
bool find(ULL x){
ULL t=x;
for (x%=M;h[x]&&v[x]^t;add(x,5));
return h[x]?1:0;
}
}h;
ULL x,tmp[N+9];
Abigail into(){
scanf("%d%d%d%d",&n,&m,&a,&b);
Pow1[0]=Pow2[0]=1;
for (int i=1;i<=m;++i) Pow1[i]=Pow1[i-1]*P1;
for (int i=1;i<=n;++i) Pow2[i]=Pow2[i-1]*P2;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
s[i][j]=s[i][j-1]*P1+rc();
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j){
s[i][j]+=s[i-1][j]*P2;
if (i>=a&&j>=b) h.insert(Hash(i-a+1,i,j-b+1,j));
}
}
Abigail getans(){
int q;
scanf("%d",&q);
while (q--){
for (int i=1;i<=a;++i){
tmp[i]=0;
for (int j=1;j<=b;++j)
tmp[i]=tmp[i]*P1+rc();
}
x=0;
for (int i=1;i<=a;++i)
x=x*P2+tmp[i];
puts(h.find(x)?"1":"0");
}
}
int main(){
into();
getans();
return 0;
}