#include<iostream>
#include<set>
#include<cassert>
#include<algorithm>
#include<queue>
using namespace std;
#define REP(i,b,n) for(int i=b;i<n;i++)
#define rep(i,n)   REP(i,0,n)

#define r_swap(x,a,b,c,d) swap(x.a,x.b);swap(x.b,x.c);swap(x.c,x.d);
const char black = '#';
const int N = 10;
const int inf = (1<<20);
char m[50][50];
enum{TOP=0,FRONT,BACK,BOTTOM,RIGHT,LEFT};
class Dice{
public:
  char top,front,right,left,back,bottom;
  bool operator<(const Dice & a)const{
    if (top != a.top)return top < a.top;
    if (front != a.front)return front < a.front;
    if (right != a.right)return right < a.right;
    if (left != a.left  )return left < a.left;
    if (back != a.back)return back < a.back;
    if (bottom != a.bottom)return bottom < a.bottom;
    return false;
  }
};

void rotate_r(Dice &x){
  r_swap(x,top,left,bottom,right);
}

void rotate_l(Dice &x){
  r_swap(x,top,right,bottom,left);
}

void rotate_f(Dice &x){
  r_swap(x,top,back,bottom,front);
}

void rotate_b(Dice &x){
  r_swap(x,top,front,bottom,back);
}

void rotate_cw(Dice &x){
  r_swap(x,back,left,front,right);
}

void rotate_ccw(Dice &x){
  r_swap(x,back,right,front,left);
}

void generate(Dice x,Dice *data){
  rep(i,6){
    rep(j,4){
      data[i*4+j]=x;
      rotate_cw(x);
    }
    if (i%2 == 0)rotate_r(x);
    else rotate_f(x);
  }
}

bool isout(int y,int x,int r,int c){
  return x==-1||y==-1||x==c||y==r;
}

int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
int next[6][4]={
  FRONT,RIGHT,BACK,LEFT,
  BOTTOM,RIGHT,TOP,LEFT,
  TOP,RIGHT,BOTTOM,LEFT,
  BACK,RIGHT,FRONT,LEFT,
  BACK,TOP,FRONT,LEFT,
  BACK,BOTTOM,FRONT,TOP
};

bool validcheck(int m[N][N],int y,int x){
  int now = m[y][x];
  rep(l,4){
    bool isok=true;
    rep(i,4){
      int nex=x+dx[i],ney=y+dy[i];
      if (isout(ney,nex,N,N) || m[ney][nex] == -1)continue;
      if (m[ney][nex] != next[now][(l+i)%4]){
	isok=false;
	break;
      }
    }
    if (isok)return true;
  }
  return false;
}

Dice generate(int *y,int *x,char *color){
  int array[]={0,1,2,3,4,5};
  int lmost=inf,dmost=inf;
  int m[10][10]={-1};
  rep(i,6){
    lmost=min(lmost,x[i]);
    dmost=min(dmost,y[i]);
  }
  rep(i,6){
    x[i]-=lmost;
    y[i]-=dmost;
  }

  do{
    rep(i,10)rep(j,10)m[i][j]=-1;
    rep(i,6){
      m[y[i]][x[i]]=array[i];
    }
    bool isok=true;
    rep(i,6){
      if (!validcheck(m,y[i],x[i])){
	isok=false;
	break;
      }
    }
    if (isok){
      Dice ret;
      //answer construction
      rep(i,6){
	switch(m[y[i]][x[i]]){
	case TOP:
	  ret.top=color[i];
	  break;
	case FRONT :
	  ret.front=color[i];
	  break;
	case BOTTOM:
	  ret.bottom=color[i];
	  break;
	case BACK:
	  ret.back=color[i];
	  break;
	case RIGHT:
	  ret.right=color[i];
	  break;
	case LEFT:
	  ret.left=color[i];
	  break;
	default:
	  assert(false);
	};
      }
      return ret;
    }
  }while(next_permutation(array,array+6));
  assert(false);
}

void output(Dice &a){
  cout <<"TOP "<< a.top << endl;
  cout <<"FRONT "<<a.front << endl;
  cout <<"BOTTOM " << a.bottom << endl;
  cout <<"BACK " << a.back << endl;
  cout <<"RIGHT " << a.right << endl;
  cout <<"LEFT " << a.left << endl;
}

bool vis[50][50];

void dfs(int y,int x,int r,int c,int *ally,int *allx,char *allc,int &p){
  if (vis[y][x])return;
  if (m[y][x]== '.')return;
  vis[y][x]=true;
  ally[p]=y;
  allx[p]=x;
  allc[p]=m[y][x];
  p++;
  rep(i,4){
    int nex=x+dx[i],ney=y+dy[i];
    if (isout(ney,nex,r,c)||m[ney][nex] == '.')continue;
    dfs(ney,nex,r,c,ally,allx,allc,p);
  }
}

void getall(Dice *in,int r,int c){
  rep(i,r)rep(j,c)vis[i][j]=false;
  int now=0;
  rep(i,r){
    rep(j,c){
      if (m[i][j] != '.' && !vis[i][j]){
	int allx[6],ally[6];
	char allc[6];
	int tmpp=0;
	dfs(i,j,r,c,ally,allx,allc,tmpp);
	in[now++]=generate(ally,allx,allc);
	//output(in[now-1]);
      }
    }
  }
}

Dice all[8][24];

bool mustblack(int i,Dice &a){
  switch(i){
  case 0:
    return a.right == black && a.back == black && a.bottom == black;
    break;
  case 1:
    return a.left == black && a.back == black && a.bottom == black;
    break;
  case 2:
    return a.front == black && a.right == black && a.bottom == black;
    break;
  case 3:
    return a.front == black && a.left == black && a.bottom == black;
    break;
  case 4:
    return a.right == black && a.top == black && a.back == black;
    break;
  case 5:
    return a.left == black && a.top == black && a.back == black;
    break;
  case 6:
    return a.right == black && a.top == black && a.front == black;
    break;
  case 7:
    return a.left == black && a.top == black && a.front == black;
    break;
  };
  assert(false);
}

bool allsame(Dice *in,int a,int b,int c,int d,int tar){
  switch(tar){
  case FRONT:
    return in[a].front==in[b].front && in[a].front==in[c].front&&in[a].front == in[d].front;
    break;
  case TOP:
    return in[a].top==in[b].top && in[a].top==in[c].top&&in[a].top == in[d].top;
    break;
  case RIGHT:
    return in[a].right==in[b].right && in[a].right==in[c].right&&in[a].right == in[d].right;
    break;
  case LEFT:
    return in[a].left==in[b].left && in[a].left==in[c].left&&in[a].left == in[d].left;
    break;
  case BACK:
    return in[a].back==in[b].back && in[a].back==in[c].back&&in[a].back == in[d].back;
    break;
  case BOTTOM:
    return in[a].bottom==in[b].bottom && in[a].bottom==in[c].bottom&&in[a].bottom == in[d].bottom;
    break;
  }
  return false;
}

bool solve(int now,Dice *ans){
  if (now == 5+1){
    if (!allsame(ans,0,1,4,5,FRONT))return false;
  }else if (now == 3+1){
    if (!allsame(ans,0,1,2,3,TOP))return false;
  }else if (now == 7+1){
    if (!allsame(ans,1,3,5,7,RIGHT))return false;
    if (!allsame(ans,4,5,6,7,BOTTOM))return false;
    if (!allsame(ans,2,3,6,7,BACK))return false;
  }else if (now == 6+1){
    if (!allsame(ans,0,2,4,6,LEFT))return false;
  }

  if (now == 8){
    char hoge[6]={ans[1].front,ans[1].top,ans[3].right,ans[2].left,
		  ans[3].back,ans[5].bottom};
    Dice a[8];
    rep(i,8)a[i]=ans[i];
    rep(i,6){
      if (hoge[i] == '#')return false;
      REP(j,i+1,6){
	if (hoge[i] == hoge[j])return false;
      }
    }

    return true;
  }

  rep(i,24){
    if (!mustblack(now,all[now][i]))continue;

    ans[now]=all[now][i];
    if (solve(now+1,ans))return true;
  }
  return false;
}


bool solve(int r,int c){
  Dice in[8];
  Dice ans[8];
  int array[8]={0,1,2,3,4,5,6,7};
  getall(in,r,c);

  do{
    rep(i,8){
      generate(in[array[i]],all[i]);
    }
    rep(i,24){
      //output(all[0][i]);
      Dice a = all[0][i];
      //cout << a.right <<" " << a.back <<" " << a.bottom << endl;
    }
    //cout << endl;
    if (solve(0,ans))return true;
  }while(next_permutation(array+1,array+8));
  return false;
}

main(){
  int r,c;
  cin>>r>>c;
  rep(i,r)cin>>m[i];
  if (solve(r,c))cout <<"Yes"<<endl;
  else cout <<"No"<<endl;
}

Measured memory_bytes: 844