1312-Where's Wally

問題概要

符号化された二次元のビット列が2つ与えられる.
ビット列を回転や反転出来るとするとき,ビット列1はビット列2に何箇所で一致するか.

解法

二次元パターンマッチング.
ラビンカープ法を二次元に適用する

実装(C++)

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>

using namespace std;
long long mod_pow(long long a,long long n,long long M){
	if(n==0)return 1;
	long long res=mod_pow(a*a%M,n/2,M);
	if(n&1)res=res*a%M;
	return res;
}
template <int B,int M>
class RollingHash{
private:
	int value;
	int hash_back;
public:
	int operator()() const{
		return value;
	}
	void set_length(int p_){
		hash_back=mod_pow(B,p_-1,M);;
	}
	explicit RollingHash(int p=0){value=0;set_length(p);}
	void add(int v){//後ろにvを追加する
		value=((long long)value*B+v)%M;
	}
	void remove(int v){
		value=(((long long)value+M-(long long)hash_back*v%M))%M;
	}
	bool operator==(const RollingHash &a){
		return a.value==value;
	}
};

int W,H,P;
char MAP[1000][1000];//検索されるもの
char PATTERN[8][100][100];//検索するもの

char tmpin[600];
inline int get_value(const char a){
	if(a>='A'&&a<='Z')return a-'A';
	if(a>='a'&&a<='z')return a-'a'+26;
	if(a>='0'&&a<='9')return a-'0'+52;
	if(a=='+')return 62;
	if(a=='/')return 63;
	cerr<<"不正な入力です:"<<a<<endl;
	return -1;
}
void rotate(int src,int dst){
	for(int x=0;x<P;x++){
		for(int y=0;y<P;y++){
			PATTERN[dst][x][y]=PATTERN[src][P-1-y][x];
		}
	}
}
void mirror(int src,int dst){
	for(int x=0;x<P;x++){
		for(int y=0;y<P;y++){
			PATTERN[dst][x][y]=PATTERN[src][y][x];
		}
	}
}
bool input(){
	scanf("%d%d%d",&W,&H,&P);
	if((W|H|P)==0)return false;
	for(int i=0;i<H;i++){
		scanf("%s",tmpin);
		for(int x=0;x<W;x+=6){
			int v=get_value(tmpin[x/6]);
			for(int k=0;k<6;k++){
				if(x+k<W){
					MAP[x+k][i]=(v>>(5-k))&1;
				}
			}
		}
	}
	for(int i=0;i<P;i++){
		scanf("%s",tmpin);
		for(int x=0;x<P;x+=6){
			int v=get_value(tmpin[x/6]);
			for(int k=0;k<6;k++){
				if(x+k<P){
					PATTERN[0][x+k][i]=(v>>(5-k))&1;
				}
			}
		}
	}
	mirror(0,4);
	for(int i=0;i<3;i++){
		rotate(i,i+1);
		rotate(4+i,4+i+1);
	}
	return true;
}
typedef RollingHash<7,35729> RH1;
typedef RollingHash<36067,1000000009> RH2;
RH1 PAT[8][100];
RH1 MAP_W[1000][1000];
int rabin_karp(int x){
	RH2 find_hash[8],exp_hash(P);
	for(int i=0;i<8;i++){
		find_hash[i].set_length(P);
	}
	int i;
	if(H<P)return 0;
	for(i=0;i<P;i++){
		for(int k=0;k<8;k++){
			find_hash[k].add(PAT[k][i]());
		}
		exp_hash.add(MAP_W[x][i]());
	}
	i=0;
	int ans=0;
	while(1){
		bool search=false;
		for(int k=0;k<8;k++){
			if(find_hash[k]==exp_hash){
				//文字列発見
				search=1;
			}
		}
		ans+=search;
		if(i>=H-P)break;
		exp_hash.remove(MAP_W[x][i]());
		exp_hash.add(MAP_W[x][i+P]());
		i++;
	}
	return ans;
}
//二次元パターンマッチング
int pattern_match(){
	for(int k=0;k<8;k++){

		for(int y=0;y<P;y++){
			RH1 tmp(P);
			for(int x=0;x<P;x++){
				tmp.add(PATTERN[k][x][y]);
			}
			PAT[k][y]=tmp;
		}
	}
	for(int y=0;y<H;y++){
		RH1 tmp(P);
		for(int x=0;x<P;x++){
			tmp.add(MAP[x][y]);
		}
		int x=0;
		while(1){
			MAP_W[x][y]=tmp;
			if(x>=W-P)break;
			tmp.remove(MAP[x][y]);
			tmp.add(MAP[x+P][y]);
			x++;
		}
	}
	int res=0;
	for(int x=0;x<=W-P;x++){
		res+=rabin_karp(x);
	}
}
int main(){
	while(input()){
		cout<<pattern_match()<<endl;

	}

}