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