#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
#include <numeric>
#include <map>

template <class T, class U>
inline bool chmin(T &lhs, const U &rhs) {
  if (lhs > rhs) {
    lhs = rhs;
    return true;
  }
  return false;
}

template <class T, class U>
inline bool chmax(T &lhs, const U &rhs) {
  if (lhs < rhs) {
    lhs = rhs;
    return true;
  }
  return false;
}

template <class T>
struct fix_point: private T {
  explicit constexpr fix_point(T &&func): T(std::forward<T>(func)) { }
  template <class... Args>
  constexpr decltype(auto) operator () (Args &&... args) const {
    return T::operator()(*this, std::forward<Args>(args)...);
  }
};

template <class T>
constexpr decltype(auto) make_fix_point(T &&func) {
  return fix_point<T>{std::forward<T>(func)};
}

int main() {
  int H, W;
  std::cin >> H >> W;
  std::vector<std::string> grid(H), sorted(H);
  for (auto &s: grid) {
    std::cin >> s;
  }
  auto encode = [&](std::string str) {
    std::sort(str.begin(), str.end());
    return str;
  };
  std::vector<std::vector<int>> group;
  {
    std::map<std::string, std::vector<int>> map;
    for (int i = 0; i < H; ++i) {
      map[encode(grid[i])].push_back(i);
    }
    group.reserve(map.size());
    int odd_index = -1;
    for (const auto &p: map) {
      if (p.second.size() % 2 == 1) {
        if (odd_index != -1) {
          std::cout << "NO\n";
          return 0;
        }
        odd_index = group.size();
      }
      group.push_back(p.second);
    }
    if (odd_index != -1) {
      group[odd_index].swap(group.back());
    }
  }
  std::vector<std::pair<int, int>> pair;
  std::vector<std::vector<bool>> used(group.size());
  for (int i = 0; i < group.size(); ++i) {
    used[i].assign(group[i].size(), false);
  }
  bool answer = false;
  auto call_rollback = [&](auto dfs, int i, int j, int k) {
    pair.emplace_back(group[i][j], group[i][k]);
    used[i][j] = used[i][k] = true;
    dfs(i);
    pair.pop_back();
    used[i][j] = used[i][k] = false;
  };
  std::vector<std::string> built(H);
  auto solve = [&] {
    std::map<std::string, int> map;
    for (int j = 0; j < W; ++j) {
      std::string cur;
      for (int i = 0; i < H; ++i) {
        cur += built[i][j];
      }
      ++map[cur];
    }
    bool found = false;
    for (const auto &p: map) {
      auto s = std::string(p.first.rbegin(), p.first.rend());
      if (p.first == s) {
        if (p.second % 2 == 1) {
          if (found) {
            return false;
          }
          found = true;
        }
      }
      auto it = map.find(s);
      if (it == map.end()) {
        return false;
      }
      else {
        if (p.second != it -> second) {
          return false;
        }
      }
    }
    return true;
  };
  make_fix_point([&](auto dfs, int i) -> void {
    if (i == group.size()) {
      for (int j = 0; j < pair.size(); ++j) {
        built[j] = grid[pair[j].first];
        built[H - j - 1] = grid[pair[j].second];
      }
      if (solve()) {
        answer = true;
      }
    }
    else {
      int l;
      for (l = 0; l < group[i].size(); ++l) {
        if (!used[i][l]) {
          break;
        }
      }
      if (l == group[i].size()) {
        dfs(i + 1);
        return;
      }
      bool found = false;
      int r;
      for (r = l + 1; r < group[i].size(); ++r) {
        if (!used[i][r]) {
          call_rollback(dfs, i, l, r);
          found = true;
        }
      }
      if (!found) {
        call_rollback(dfs, i, l, l);
      }
    }
  })(0);
  std::cout << (answer ? "YES" : "NO") << '\n';
  return 0;
}


Measured memory_bytes: 256