POJ 3690 星座
传送门:POJ 3690 星座
题意
给定一个由'*'和'0'组成的,大小为N*M的匹配对象和T个大小为P*Q的匹配模式。请输入在匹配对象中至少出现过一次的匹配模式的个数。(多组数据,输入结尾为五个0)
题解
二维哈希
二维网格的匹配,因为匹配模式的大小给定,为P*Q,可以运用循环哈希。首先先把每一行看成一个字符串,求出从每个位置开始,长度为Q的子串的哈希值,每行将会有N-P+1个哈希值。然后再把得到的哈希值按照列看成一个字符串,求出从每个位置开始,长度为P的子串的哈希值。在两次哈希中,可以使用不同的哈希值来避免冲突。
于是我们可以得到所有匹配模式和匹配对象中每个P*Q子矩阵的哈希值。先把匹配模式的hash值放入multiset中,erase掉每个匹配对象子矩阵的hash值,最后的答案就是T-multiset.size()。
注
1、使用multiset而不是set。如果出现了两个相同的匹配模式P*Q,而最终没有删掉他们,用set就会产生错误。
代码
#include<iostream>
#include<set>
#include<cstdio>
using namespace std;
typedef unsigned long long ull;
const ull base1=9973;
const ull base2=1e9+7;
const int Size=1010;
int T;
int n,m,t,p,q;
char graph[Size][Size];
char patterns[110][Size][Size];
ull hash[Size][Size],tmp[Size][Size];
multiset<ull> ms;
void Hash(char a[Size][Size],int n,int m){
ull t1=1;
for(int j=0;j<q;j++) t1*=base1;
//按行方向计算哈希值
for(int i=0;i<n;i++){
ull now=0;
for(int j=0;j<q;j++) now=now*base1+a[i][j];
for(int j=0;j+q<=m;j++){
tmp[i][j]=now;
now=now*base1-a[i][j]*t1+a[i][j+q];
}
}
ull t2=1;
for(int i=0;i<p;i++) t2*=base2;
//按列方向计算哈希值
for(int j=0;j+q<=m;j++){
ull now=0;
for(int i=0;i<p;i++) now=now*base2+tmp[i][j];
for(int i=0;i+p<=n;i++){
hash[i][j]=now;
now=now*base2-tmp[i][j]*t2+tmp[i+p][j];
}
}
}
void solve(){
for(int i=0;i<t;i++){
Hash(patterns[i],p,q);
ms.insert(hash[0][0]);
}
Hash(graph,n,m);
for(int i=0;i+p<=n;i++){
for(int j=0;j+q<=m;j++){
ms.erase(hash[i][j]);
}
}
int ans=t-ms.size();
printf("Case %d: %d\n",T,ans);
}
int main(){
while(scanf("%d%d%d%d%d",&n,&m,&t,&p,&q)&&(n||m||t||p||q)){
T++;
ms.clear();
for(int i=0;i<n;i++){
scanf("%s",graph[i]);
}
for(int i=0;i<t;i++){
for(int j=0;j<p;j++){
scanf("%s",patterns[i][j]);
}
}
solve();
}
return 0;
}

浙公网安备 33010602011771号