Fork me on GitHub

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;
}
posted @ 2020-03-12 11:25  qjy_73  阅读(139)  评论(0)    收藏  举报