#include "bits/stdc++.h"
#include <random>
using namespace std;

class tree {
public:
	int id;
	vector<unique_ptr<tree>>chs;
	tree(const int id_):chs() {
		id = id_;
	}
	pair<long long int, long long int>gethash() {
		pair<long long int, long long int>ans(1e9 + 7, 1e9 + 9);
		vector<pair<long long int, long long  int>>hashes;
		for (auto& ch : chs) {
			hashes.push_back(ch->gethash());
		}
		sort(hashes.begin(), hashes.end());
		for (auto p : hashes) {
			std::mt19937 mt(p.first/2+p.second/3);
			ans.first = ans.first*static_cast<long long int>(1e11 + 7) + mt();
			ans.second = ans.second*static_cast<long long int>(1e11 + 9) + 37*mt();
		}
		return ans;
	}

};
unique_ptr<tree> mktree(const int now,const vector<vector<int>>&edges) {
	unique_ptr<tree> t(make_unique<tree>(now));
	for (auto e : edges[now]) {
		t->chs.push_back(mktree(e, edges));
	}
	return t;
}

struct UnionFind {
	vector<int> data;
	UnionFind(int size) : data(size, -1) { }
	bool unionSet(int x, int y) {
		x = root(x); y = root(y);
		if (x != y) {
			if (data[y] < data[x]) swap(x, y);
			data[x] += data[y]; data[y] = x;
		}
		return x != y;
	}
	bool findSet(int x, int y) {
		return root(x) == root(y);
	}
	int root(int x) {
		return data[x] < 0 ? x : data[x] = root(data[x]);
	}
	int size(int x) {
		return -data[root(x)];
	}
};


int dx[8] = { -1,-1,-1, 0, 1, 1, 1, 0 };
int dy[8] = { -1, 0, 1, 1, 1, 0,-1,-1 };
struct aa {
	int x;
	int y;
	bool flag;
};
int H, W;
bool valid(const int y, const int x) {
	return y >= 0 && y < H + 2 && x >= 0 && x < W + 2;
}
pair<long long int,long long int> f(vector<vector<int>>&field) {
	for (int i = 0; i < H; ++i) {
		string st; cin >> st;
		for (int j = 0; j < W; ++j) {
			int black = st[j] == '#';
			field[i + 1][j + 1] = black;
		}
	}
	UnionFind uf((W + 2)*(H + 2));
	for (int y = 0; y < H + 2; ++y) {
		for (int x = 0; x < W + 2; ++x) {
			for (int way = 0; way < 8; ++way) {
				if (field[y][x] == 0 && way % 2 == 0)continue;
				const int nextx = x + dx[way];
				const int nexty = y + dy[way];
				if (valid(nexty, nextx)) {
					if (field[nexty][nextx] == field[y][x]) {
						uf.unionSet(nexty*(W + 2) + nextx, y*(W + 2) + x);
					}
				}
			}
		}
	}
	for (int y = 0; y < H + 2; ++y) {
		for (int x = 0; x < W + 2; ++x) {
			if (field[y][x] = uf.root(y*(W + 2) + x));
		}
	}

	vector<vector<int>>comes(field.size(), vector<int>(field[0].size(), 0));

	map<int, vector<pair<int, int>>>mp;
	for (int y = 0; y < H + 2; ++y) {
		for (int x = 0; x < W + 2; ++x) {
			mp[field[y][x]].push_back(make_pair(x, y));
		}
	}
	queue<aa>que;
	for (auto m : mp[field[0][0]]) {
		comes[m.second][m.first] = true;
		que.push(aa{ m.first,m.second,false });
	}
	int num = 0;
	vector<vector<int>>edges(12000);
	set<int>used;
	used.emplace(field[0][0]);
	while (!que.empty()) {
		aa atop(que.front());
		que.pop();
		int nowx = atop.x;
		int nowy = atop.y;
		
		int c = field[nowy][nowx];
		auto& nedges(edges[field[nowy][nowx]]);
		for (int way = 0; way < 8; ++way) {
			if (way % 2 == 0 && !atop.flag)continue;
			int nextx = nowx + dx[way];
			int nexty = nowy + dy[way];
			if(valid(nexty,nextx)){
				if (comes[nexty][nextx] ==0) {
					if (used.find(field[nexty][nextx]) == used.end()) {
						used.emplace(field[nexty][nextx]);
						num++;
						edges[field[nowy][nowx]].push_back(field[nexty][nextx]);
						for (auto m : mp[field[nexty][nextx]]) {
							comes[m.second][m.first] = true;
							que.push(aa{ m.first,m.second,!atop.flag });
						}
					}
				}
			}
		}
	}
	auto t = mktree(field[0][0], edges);
	return t->gethash();
}
int main() {
	while (1) {
		pair<long long int,long long int> ans1, ans2;
		{
			cin >> H >> W;
			if (!H)break;
			vector<vector<int>>field(H + 2, vector<int>(W + 2));
			
			ans1 = f(field);
		}
		{
			cin >> H >> W;
			if (!H)break;
			vector<vector<int>>field(H + 2, vector<int>(W + 2));

			ans2 = f(field);
		}
		if (ans1 == ans2)cout << "yes" << endl;
		else cout << "no" << endl;
	}
	return 0;
}

Measured memory_bytes: 3524