import std.algorithm;
import std.array;
import std.conv;
import std.math;
import std.range;
import std.stdio;
import std.string;

void main() {
  auto z = readln.strip.splitter.map! (i => i.to!int).array;
  int h = z[0];
  int w = z[1];
  string [] a = new string[h];
  foreach (t; 0 .. h) {
    a[t] = readln[0 .. w];
  }
  int s = 0;
  auto x = new int[w];
  auto order = new int[w];
  int k = 0, i = 0, j = w - 1;
  while (i < j) {
    order[k++] = i;
    order[k++] = j;
    i++;j--;
  }
  if (k < w) order[k++] = i;
  bool cmp (int i, int j, int l) {
    foreach (k; 0 .. l) {
      if (a[i][x[k]] != a[j][x[w-1-k]]) {
        return false;
      }
    }
    return true;
  }
  bool check (int l) {
    int st = 0;
    int t = h;
    while (t > 1) {
      int old_t = t;
      loop: foreach (i; 0 .. h) if (!(st & (1 << i))) {
        foreach (j; i + 1 .. h) if (!(st & (1 << i))) {
          if ((l == w) ? cmp (i, j, l) : cmp (i, j, l / 2) && cmp (j, i, l / 2)) {
            st |= (1 << i) | (1 << j);
            t -= 2;
            break loop;
          }
        }
      }
      if (old_t == t) return false;
    }
    if (t == 1) {
      foreach (i; 0 .. h) if (!(st & (1 << i))) {
        return (l == w) ? cmp (i, i, l) : cmp (i, i, l / 2);
      }
    }
    return true;
  }
  bool go (int k, int st) {
    if (k == w) {
      return check (w); 
    }
    if (!(k & 1)) {
      if (!check (k)) return false;
    }
    foreach (i; 0 .. w) {
      if (!(st & (1 << i))) {
        x[order[k]] = i;
        if (go (k + 1, st | (1 << i))) return true;
      }
    }
    return false;
  }
  if (go (0, 0)) {
    writeln ("YES");
  } else {
    writeln ("NO");
  }
}

