POJ3690 Constellations(二维Hash)

题目链接

Description

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.

Input

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.

Output

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.

Sample Input

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

Sample Output

Case 1: 1
Case 2: 2

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

在《挑战程序设计竞赛(第2版)》看到的解法是二维hash,觉得很神奇,想法很妙。哈希可参考POJ3461 && LOJ#10033 Oulipo(字符串HASH)。

首先把每一行看成一个字符串,计算从每个位置开始长度为Q的字符串子串的哈希值。然后再把得到的哈希值在列方向上看成一个字符串,计算从每个位置开始长度为P的字符串子串的哈希值。

当然,在这两次哈希值的计算中,选用了不同的基数。

用tmp[i][j]表示第i行第j个位置开始长度为Q的字符串的哈希值(用来暂存)。
用hash[i][j]表示第j列第i个位置开始长度为P的(哈希串,姑且这么称呼吧,若有雷同,纯属巧合)的哈希值。
这样将匹配模式和匹配对象的比较问题转化为了哈希值的比较问题。
代码中将哈希值保存在multiset中,不选用set的原因是multiset允许保存重复值,而匹配模式有相同的可能,故采用multiset。

AC代码:

//CSDN博客:https://blog.csdn.net/qq_40889820
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define ull unsigned long long
#define Pi 3.141592654
using namespace std;
int N,M,T,P,Q; 
char field[1000][1000];//匹配对象
char patterns[1000][1000];//匹配模式 
ull Hash[1000][1000],tmp[1000][1000];
void compute_hash(char a[1000][1000],int n,int m)
{
	const ull B1=1e9+7;
	const ull B2=1e9+9;
	
	ull t1=1;//B1的Q次方
	for(int i=0;i se;
	int count=0;
	while(cin>>N>>M>>T>>P>>Q)
	{
		se.clear();//注意注意
		if(N==M&&N==T&&N==T&&N==P&&N==Q) break;
		for(int i=0;i>field[i];
		//将所有匹配模式的哈希值放入一个multiset中,允许元素重复,方便统计、删除元素 
		for(int t=1;t<=T;t++)
		{
			for(int i=0;i>patterns[i];
			compute_hash(patterns,P,Q);
			se.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++)
				se.erase(Hash[i][j]);
				
		int ans=T-se.size();
		cout<<"Case "<<++count<<": "<

 

你可能感兴趣的:(#,>>>>字符串算法,∨∨Acm)