#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<climits>
#include<algorithm>
#include<vector>
#include<deque>
#include<map>
#include<set>
#include<unordered_map>
#include<unordered_set>

#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)

using namespace std;

string tree_to_string(vector<vector<int>> &tree,int cur,int prev) {
  vector<string> vec;
  rep(i,(int)tree[cur].size()) {
    if( tree[cur][i] == prev ) continue;
    vec.push_back(tree_to_string(tree,tree[cur][i],cur));
  }
  if( vec.empty() ) return "()";
  sort(vec.begin(),vec.end());
  string ret = "(";
  rep(i,(int)vec.size()) {
    ret += vec[i];
  }
  ret += ")";
  return ret;
}

bool rootedTreeIsomorphism(vector<vector<int>> &t1,int root1,vector<vector<int>> &t2,int root2) {
  return tree_to_string(t1,root1,-1) == tree_to_string(t2,root2,-1);
}

int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};

struct UnionFind {
  int par[100100];
  UnionFind(int size=100100) { rep(i,size) par[i] = i; }
  int find(int x) { 
    assert( x < 100100 );
    if( x == par[x] ) return x;
    return par[x] = find(par[x]);
  }
  void unit(int x,int y) {
    x = find(x), y = find(y);
    if( x != y ) par[x] = y;
  }
};

struct Picture {
  int h,w;
  vector<string> p;
  bool modified;

  Picture(int h=0,int w=0,vector<string> p=vector<string>()) : h(h), w(w), p(p), modified(false) {}

  bool isValid(int x,int y) { return 0 <= x && x < w && 0 <= y && y < h; }
  
  void modify() {
    if( modified ) {
      cerr << "already modified" << endl;
      return;
    }
    modified = true;
    vector<string> new_p(h+2,string(w+2,'.'));
    rep(i,h) {
      rep(j,w) {
	new_p[i+1][j+1] = p[i][j]; 
      }
    }
    p = new_p;
    h += 2;
    w += 2;
  }

  void print() {
    rep(i,h) cout << p[i] << endl;
  }

  vector<vector<int>> to_tree() {
    UnionFind uf(h*w);
    rep(i,h) rep(j,w) {
      if( p[i][j] == '.' ) {
	rep(k,4) {
	  int nx = j + dx[k];
	  int ny = i + dy[k];
	  if( !isValid(nx,ny) ) continue;
	  if( p[ny][nx] != '.' ) continue;
	  uf.unit(j+i*w,nx+ny*w);
	}
      } else {
	REP(dy_b,-1,2) REP(dx_b,-1,2) {
	  if( dx_b == 0 && dy_b == 0 ) continue;
	  int nx = j + dx_b;
	  int ny = i + dy_b;
	  if( !isValid(nx,ny) ) continue;
	  if( p[ny][nx] != '#' ) continue;
	  uf.unit(j+i*w,nx+ny*w);
	}
      }
    }
    
    vector<bool> used(h*w,false);

    map<int,int> toIndex;
    int idx = 0;
    rep(i,h) rep(j,w) {
      if( !toIndex.count(uf.find(j+i*w)) ) {
	toIndex[uf.find(j+i*w)] = idx++;
      }
    }
    vector<vector<int>> tree(idx,vector<int>());

    rep(i,h) rep(j,w) {
      int cur = uf.find(j+i*w);
      if( used[cur] ) continue;
      assert( toIndex.count(cur) );
      int s = toIndex[cur];
      rep(k,h) rep(l,w)  {
	int next = uf.find(l+k*w);
	if( next != cur ) continue;
	REP(dy_1,-1,2) REP(dx_1,-1,2){
	  if( dx_1 == 0 && dy_1 == 0 ) continue;
	  int nx = l + dx_1;
	  int ny = k + dy_1;
	  if( !isValid(nx,ny) ) continue;
	  if( p[ny][nx] == p[i][j] ) continue;
	  int id = uf.find(nx+ny*w);
	  if( used[id] ) continue;
	  assert( toIndex.count(id) );
	  int t = toIndex[id];
	  if( s == t ) continue;
	  tree[s].push_back(t);
	}
      }
      used[cur] = true;
    }

    rep(i,idx) {
      sort(tree[i].begin(),tree[i].end());
      tree[i].erase(unique(tree[i].begin(),tree[i].end()),tree[i].end());
    }

    return tree;
  }
  
};

void compute(Picture *picts) {
  rep(i,2) picts[i].modify();
  
  vector<vector<int>> tree1 = picts[0].to_tree();
  vector<vector<int>> tree2 = picts[1].to_tree();

  cout << (rootedTreeIsomorphism(tree1,0,tree2,0)?"yes":"no") << endl;
}

int main() {
  while( 1 ) {
    Picture picts[2];
    rep(i,2) {
      cin >> picts[i].h >> picts[i].w;
      if( i == 0 && picts[i].h == 0 && picts[i].w == 0 ) goto Fin;
      picts[i].p.resize(picts[i].h);
      rep(j,picts[i].h) cin >> picts[i].p[j];
    }
    compute(picts);
  }
 Fin:
  return 0;
}

Measured memory_bytes: 3448