#define _USE_MATH_DEFINES
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <queue>
#include <stack>
#include <list>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <complex>
#include <cmath>
#include <numeric>
#include <bitset>
#include <functional>

using namespace std;

#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
  cerr << name << ": " << arg1 << endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
  const char* comma = strchr(names + 1, ',');
  cerr.write(names, comma - names) << ": " << arg1 << " |";
  __f(comma + 1, args...);
}

typedef long long int64;
typedef pair<int, int> ii;
const int INF = 1 << 29;
const int MOD = 1e9 + 7;

const int N = 12;
char s[N][N];
bool can[N][N];
bitset<N> g[26][26];
bitset<N> a[N][N][N];
int n, m;
bool visit[N], visit2[N];

bool solve2(int now, bool center, const vector<bitset<N>>& mask) {
  if (now == m) {
    return true;
  }
  if (visit2[now]) return solve2(now + 1, center, mask);
  visit2[now] = true;
  if (m % 2 && !center && mask[now][now]) {
    if (solve2(now + 1, true, mask)) return true;
  }
  for (int i = now + 1; i < m; ++i) {
    if (visit2[i] || !mask[now][i]) continue;
    visit2[i] = true;
    if (solve2(now + 1, center, mask)) return true;
    visit2[i] = false;
  }
  return false;
}

bool solve(int now, bool center, const vector<bitset<N>>& mask) {
  if (now == n) {
    return solve2(0, false, mask);
  }
  if (visit[now]) return solve(now + 1, center, mask);
  visit[now] = true;
  if (n % 2 && !center) {
    vector<bitset<N>> nxt_mask = mask;
    bool found = false;
    for (int k = 0; k < m; ++k) {
      nxt_mask[k] &= a[now][now][k];
      if (nxt_mask[k] == 0) found = true;
    }
    if (!found && solve(now + 1, true, mask)) return true;
  }
  for (int i = now + 1; i < n; ++i) {
    if (visit[i] || !can[now][i]) continue;
    vector<bitset<N>> nxt_mask = mask;
    bool found = false;
    for (int k = 0; k < m; ++k) {
      nxt_mask[k] &= a[now][i][k];
      if (nxt_mask[k] == 0) found = true;
    }
    visit[i] = true;
    if (!found && solve(now + 1, center, nxt_mask)) return true;
    visit[i] = false;
  }
  visit[now] = false;
  return false;
}

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 0; i < n; ++i) {
    scanf("%s", s[i]);
  }
  for (int i = 0; i < n; ++i) {
    for (int j = i; j < n; ++j) {
      memset(g, 0, sizeof(g));
      for (int k = 0; k < m; ++k) {
        g[s[i][k] - 'a'][s[j][k] - 'a'].set(k);
      }
      bool found = false;
      for (int u = 0; u < 26; ++u) {
        for (int v = u + 1; v < 26; ++v) {
          if (g[u][v].count() != g[v][u].count()) found = true;
        }
      }
      can[i][j] = can[j][i] = !found;
      if (found) continue;
      for (int k = 0; k < m; ++k) {
        a[i][j][k] = g[s[j][k] - 'a'][s[i][k] - 'a'];
      }
    }
  }
  vector<bitset<N>> mask(m, (1 << m) - 1);
  bool ret = solve(0, false, mask);
  puts(ret ? "YES" : "NO");
  return 0;
}


Measured memory_bytes: 256