【C++】「2008 Asia Hefei Regional Contest Online by USTC」Constellations

【来源】

2008 Asia Hefei Regional Contest Online by USTC
USCT-1100
POJ-3690
HDU-2456
vjudge

【题目描述】

The starry sky in the summer night is one of the most beautiful things on this planet. People imagine that some groups of stars in the sky form so-called constellations. Formally a constellation is a group of stars that are connected together to form a figure or picture. Some well-known constellations contain striking and familiar patterns of bright stars. Examples are Orion (containing a figure of a hunter), Leo (containing bright stars outlining the form of a lion), Scorpius (a scorpion), and Crux (a cross).

In this problem, you are to find occurrences of given constellations in a starry sky. For the sake of simplicity, the starry sky is given as a N × M matrix, each cell of which is a ‘*’ or ‘0’ indicating a star in the corresponding position or no star, respectively. Several constellations are given as a group of T P × Q matrices. You are to report how many constellations appear in the starry sky.

Note that a constellation appears in the sky if and only the corresponding P × Q matrix exactly matches some P × Q sub-matrix in the N × M matrix.

【输入格式】

The input consists of multiple test cases. Each test case starts with a line containing five integers N, M, T, P and Q(1 ≤ N, M ≤ 1000, 1 ≤ T ≤ 100, 1 ≤ P, Q ≤ 50).
The following N lines describe the N × M matrix, each of which contains M characters ‘*’ or ‘0’.
The last part of the test case describe T constellations, each of which takes P lines in the same format as the matrix describing the sky. There is a blank line preceding each constellation.
The last test case is followed by a line containing five zeros.

【输出格式】

For each test case, print a line containing the test case number( beginning with 1) followed by the number of constellations appearing in the sky.

【样例输入】

3 3 2 2 2
*00
0**
*00

**
00

*0
**
3 3 2 2 2
*00
0**
*00

**
00

*0
0*
0 0 0 0 0

【样例输出】

Case 1: 1
Case 2: 2

【题目大意】

给定一个由’*'和’0’组成的,大小为N×M的(N行M列)的匹配对象和T个大小为P×Q的匹配模式。
请输出在匹配对象中至少出现过一次的匹配模式的个数。

【解析】

这里要做的不是字符串匹配,而是二维网格的匹配,可以用循环哈希。
首先把每一行看成一个字符串,计算从每个位置开始长度为Q的字符子串的哈希值。
然后再把得到的哈希值再列方向看成一个字符串,计算从每个位置开始长度为P的字符子串的哈希值。
这样,我们高效地得到P×Q的子阵的哈希值。
本题也可用Aho-Corasick算法解决。

【代码】

#pragma GCC optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

const int NN=1005;
const int inf=2e9;

int N,M,t,p,q,cas;
ULL hah[NN][NN],tmp[NN][NN];
char fd[NN][NN];
char pt[105][NN][NN];

inline void chash(char a[NN][NN],int n,int m) {
	const ULL b1=9973;
	const ULL b2=1e8+7;
	ULL t1=1;
	for(int i=0; i<q; i++) 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;
	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++) {
			hah[i][j]=e;
			if(i+p<n) e=e*b2-t2*tmp[i][j]+tmp[i+p][j];
		}
	}
}

inline void solve() {
	multiset<ULL> unseen;
	for(int k=0; k<t; k++) {
		chash(pt[k],p,q);
		unseen.insert(hah[0][0]);
	}
	chash(fd,N,M);
	for(int i=0; i+p<=N; i++) 
		for(int j=0; j+q<=M; j++) 
			unseen.erase(hah[i][j]);
	int ans=t-unseen.size();
	printf("Case %d: %d\n",cas,ans);
}

int main() {
	while(1) {
		cas++;
		scanf("%d%d%d%d%d",&N,&M,&t,&p,&q);
		if(N==0 && M==0 && p==0 && q==0 && t==0) break;
		memset(hah,0,sizeof(hah));
		memset(tmp,0,sizeof(tmp));
		for(int i=0; i<N; i++) scanf("%s",fd[i]);
		for(int i=0; i<t; i++) {
			for(int j=0; j<p; j++) scanf("%s",pt[i][j]);
		} 
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值