#include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)(n);i++)
#define rrep(i,n) for(int i=(int)(n)-1;i>=0;i--)
#define each(a,x) for(auto a : (x))
#define all(a) (a).begin(),(a).end()
#define chmin(a,b) ((a) = min((a),(b)))
#define chmax(a,b) ((a) = max((a),(b)))
#define in_range(x,l,r) ((l)<=(x) && (x)<(r))
#define printvec(a) rep(i,a) cout << a[i] << " \n"[i+1==(a).size()];
#define fs first
#define sc second
#define em emplace
#define eb emplace_back
#define pb push_back
#define sz size()
#define MP make_pair
using namespace std;
typedef long long ll;
typedef double D;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<string> vs;

const ll INF = 1e8;
const D EPS = 1e-8;
const ll MOD = 1e9+7;

struct edge{
  int from,to,cost,cap,rev;
  edge(int a,int b,int c,int d,int e)
    :from(a),to(b),cost(c),cap(d),rev(e){}
};

struct dinic{
  int n;
  vector< vector<edge> > graph;
  vi level,iter;

  dinic(int a=0):n(a){graph.resize(a);}

  void AddEdge(int s,int g,int c,int p){
    graph[s].pb( edge(s,g,c,p,graph[g].sz) );
    graph[g].pb( edge(g,s,-c,0,graph[s].sz-1) );
  }

  void DeleteEdge(int s, int g){
    int id = -1, rev = -1;
    rep(e,graph[s].sz){
      if(graph[s][e].to == g){
	id = e; rev = graph[s][e].rev;
      }
    }
    if(id<0)return;

    swap(graph[s][id],graph[s].back());
    swap(graph[g][rev],graph[g].back());
    graph[s][id].rev = rev;
    graph[g][rev].rev = id;
    graph[s].pop_back(); graph[g].pop_back();
  }

  void bfs(int s){
    level = vi(n,-1);
    level[s] = 0;
    queue<int> q; q.push(s);
    while(q.sz){
      int v = q.front(); q.pop();
      for(edge e: graph[v]){
	if(e.cap > 0 && level[e.to] < 0){
	  level[e.to] = level[v] + 1;
	  q.push(e.to);
	}
      }
    }
  }
  
  int dfs(int v, int t, int f){
    if(v==t)return f;
    for(int &i = iter[v];i<(int)graph[v].sz;i++){
      edge &e = graph[v][i];
      if(e.cap > 0 && level[v] < level[e.to]){
	int d = dfs(e.to,t,min(f,e.cap));
	if(d > 0){
	  e.cap -= d;
	  graph[e.to][e.rev].cap += d;
	  return d;
	}
      }
    }
    return 0;
  }
  
  int max_flow(int s, int t){
    int res = 0;
    for(;;){
      bfs(s);
      if(level[t]<0)return res;
      iter = vi(n,0);
      int f;
      while((f=dfs(s,t,INF))>0)res += f;
    }
  }
};

/*
bool check(const vector<string>& g, const vector<pair<int, int>>& ids) {
  int w = g[0].size();

  vector<vector<int>> graph(w, vector<int>(w, 1));
  rep(i, w) graph[i][i] = 0;

  for(pair<int, int> p : ids) {
    string a = g[p.fs], b = g[p.sc];
    rep(i, w) {
      rep(j, w) {
	if ( !(a[i] == b[j] && a[j] == b[i]) ) {
	  graph[i][j] = 0;
	}
      }
    }
  }

  dinic mf(2*w+2);
  int s = 2*w, t = s + 1;
  rep(i, w) {
    mf.AddEdge(s, i, 0, 1);
    mf.AddEdge(w + i, t, 0, 1);
    rep(j, w) {
      if (graph[i][j]) {
	mf.AddEdge(i, w + j, 0, 1);
      }
    }
  }
	    
  return mf.max_flow(s, t) == w / 2 * 2;
}
*/

bool check(const vector<string>& g, const vector<pair<int, int>>& ids) {
  vector<string> x(g.size());
  int pos = 0;
  for (auto p : ids) {
    if (p.fs == p.sc) {
      x[g.size()/2] = g[p.fs];
    } else {
      x[pos] = g[p.fs];
      x[g.size() - 1 - pos] = g[p.sc];
      pos++;
    }
  }

  int cnt = 0;
  vector<bool> used(x[0].size(), false);
  rep(i, x[0].size()) {
    if (used[i]) continue;
    string a;
    rep(k, x.size()) a += x[k][i];
    bool ok = false;
    for (int j = i + 1; j < (int)x[0].size(); ++j) {
      if (i == j) continue;
      if (used[j]) continue;

      string b;
      rep(k, x.size()) b += x[k][j];
      reverse(all(b));
      if (a == b) {
	ok = true;
	used[i] = true;
	used[j] = true;
	break;
      }
    }
    if (!ok) {
      string b = a;
      reverse(all(b));
      if (a != b) return false;
      used[i] = true;
      cnt++;
    }
    if (cnt > 1) return false;
  }
  return true;
}

bool rec(int d, int cnt, const vector<string>& g, const vector<vector<bool>>& match,
	 vector<pair<int, int>>& ids, vector<bool>& used) {
  if (d == 0) {
    return check(g, ids);
  }
  
  rep(i, g.size()) {
    if (used[i]) continue;
    used[i] = true;
    for (size_t j = i+1; j < g.size(); ++j) {
      if (used[j]) continue;
      if (!match[i][j]) continue;
      used[j] = true;
      ids.push_back(make_pair(i, j));

      if (rec(d - 2, cnt, g, match, ids, used)) return true;

      ids.pop_back();      
      used[j] = false;
    }

    if (d % 2 == 1 and cnt == 0) {
      ids.push_back(make_pair(i, i));
      if (rec(d - 1, cnt + 1, g, match, ids, used)) return true;
      ids.pop_back();
    }
    
    used[i] = false;
    break;
  }
  return false;
}

inline bool solve(const vector<string>& g) {
  vector<vector<bool>> match(g.size(), vector<bool>(g.size(), false));
  rep(i, g.size()) {
    string a = g[i];
    sort(all(a));
    rep(j, g.size()) {
      string b = g[j];
      sort(all(b));
      match[i][j] = (a == b);
    }
  }
  
  vector<bool> used(g.size(), false);
  vector<pair<int, int>> ids;
  return rec(g.size(), 0, g, match, ids, used);
}

int main(){
  int h, w;
  cin >> h >> w;
  vs g(h);
  rep(i, h) cin >> g[i];

  cout << (solve(g) ? "YES" : "NO") << endl;
}


Measured memory_bytes: 256