星座-POJ3690-详解

星座

给定一个由’'和’0’组成的,大小为 N x M (N行M列)的匹配对象和 T个大小为 P x Q的匹配模式。
请输出在匹配对象中至少出现过一次的匹配模式的个数。
思路:这里要做的不是字符串匹配,而是二维网络匹配,同样可以运用循环哈希,首先把每一行看成一个字符串,计算从每个位置开始长度为Q的字符串子串的哈希值。然后再把得到的哈希值在列方向看成一个字符串,计算从每一个位置开始长度为P的字符串子串的哈希值。这样,我们高效地计算得到了所有PxQ的子阵的哈希值。在两次哈希值地计算中,我们运用了不同的基数。
这里写图片描述
题意:给出一个N
M的大矩阵。在给出T个P*Q的小矩阵,问有多少个小矩阵在大矩阵中出现过。

思路:二维hash。算出整个矩阵的所有的P*Q小矩阵的hash值,同时求出目标矩阵的hash值,判断该值是否出现即可。

复杂度:N * M * K

/*
星座
给定一个由'*'和'0'组成的,大小为 N x M (N行M列)的匹配对象和 
T个大小为 P x Q的匹配模式。
请输出在匹配对象中至少出现过一次的匹配模式的个数。 
*/
#include<iostream>
#include<set>
#include<cstdio>
using namespace std; 
typedef unsigned long long ull;
const int MAX_SIZE = 1005;
const int MAX_T = 105;
//输入
int N,M,T,P,Q,tt=0;
char field[MAX_SIZE][MAX_SIZE];//匹配对象
char patterns[MAX_T][MAX_SIZE][MAX_SIZE];//匹配模式
ull hash[MAX_SIZE][MAX_SIZE],tmp[MAX_SIZE][MAX_SIZE];

//计算 a的所有 P x Q子阵对应的哈希值
void compute_hash(char a[MAX_SIZE][MAX_SIZE],int n,int m){
	const ull B1 = 9973;
	const ull B2 = 100000007;
	ull t1 = 1;//B1的 Q次方
	for(int j=0;j<Q;j++)
		t1 *= B1;
	//按行方向计算哈希值
	for(int i=0;i<n;i++){
		ull e = 0;
		for(int j=0;j<Q;j++)
			e = e * B1 + a[i][j];
		for(int j=0;j+Q<=m;j++){
			tmp[i][j] = e;
			if(j + Q < m)
				e = e * B1 - t1 * a[i][j] + a[i][j + Q]; 
		} 
	} 
	
	ull t2 = 1;// B2的 P次方
	for(int i=0;i<P;i++)
		t2 *= B2;
	//按列方向计算哈希值
	for(int j=0;j+Q<=m;j++){
		ull e = 0;
		for(int i=0;i<P;i++)
			e = e * B2 + tmp[i][j];
		for(int i=0;i+P<=n;i++){
			hash[i][j] = e;
			if(i + P < n)
				e = e * B2 - t2 * tmp[i][j] + tmp[i+P][j]; 
		} 
	} 
} 

void solve(){
		//将所有的模式的哈希值放入multiset中
	multiset<ull> unseen;
	for(int k=0;k<T;k++){
		compute_hash(patterns[k],P,Q);
		unseen.insert(hash[0][0]); 
	} 
	
	//将出现的哈希值从 multiset中删除
	compute_hash(field,N,M);
	for(int i=0;i+P<=N;i++){
		for(int j=0;j+Q<=M;j++){
			unseen.erase(hash[i][j]);
		} 
	} 
	
	//通过相减得到出现的模式的个数
	int ans = T - unseen.size();
	printf("Case %d: %d\n",++tt,ans); 
} 
/*
3 3
*00
0**
*00
2 2 2
**
00
*0
**
*/
int main(){
	while(scanf("%d %d %d %d %d", &N, &M, &T, &P, &Q) != EOF){ 
		if(!N && !M && !T && !P && !Q)
			break; 
		for(int i=0;i<N;i++){
			scanf("%s",field[i]);
		} 
		for(int i=0;i<T;i++){
			for(int j=0;j<P;j++){
				scanf("%s",patterns[i][j]);
			} 
		} 
		solve();
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值