#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
#include <map>
#include <set>
#include <cstring>
#include <string>
#include <cctype>
#include <cassert>
#include <queue>
#include <unordered_set>
#include <unordered_map>

using namespace std;

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define rep(i,n) for(int i = 0; i < (n); i++)
#define repr(i,b,e) for(int i = (b); i <= (e); i++)
#define INF (1001001001)
#define EPS (1e-15)

#define pr(x) do{cerr << (#x) << " = " << (x) << endl;}while(0)
#define pri(x,i) do{cerr << (#x) << "[" << i << "] = " << (x[i]) << endl;}while(0)
#define pra(x) do{rep(__i,(x).size()) pri(x,__i);}while(0)
#define pran(x,n) do{rep(__i,n) pri(x,__i);}while(0)
#define pral(x) do{cerr << (#x) << " =";rep(__i,(x).size()) cerr<<" "<<(x[__i]);cerr<<endl;}while(0)
#define prar(x,b,e) do{repr(__i,b,e) pri(x,__i);}while(0)

typedef long long ll;
typedef pair<int, int> pint;
typedef vector<int> vint;
typedef vector<pint> vpint;

template<class T> ostream& operator<<(ostream &os, const vector<T> &t) {os<<"{"; rep(i,t.size()) {os<<t[i]<<",";} os<<"}"<<endl; return os;}
template<class T,size_t n> ostream& operator<<(ostream &os, const array<T,n> &t) {os<<"{"; rep(i,n) {os<<t[i]<<",";} os<<"}"<<endl; return os;}
template<class S, class T> ostream& operator<<(ostream &os, const pair<S,T> &t) { return os<<"("<<t.first<<","<<t.second<<")";}
template<class T> inline bool CHMAX(T &l,const T &r){return l<r?l=r,1:0;}
template<class T> inline bool CHMIN(T &l,const T &r){return l>r?l=r,1:0;}

ll MOD = 1000000007;

function<void(int,int)> tensi_no_3p=[&](int v,int d){
};

int in() {
	int a;
	scanf("%d ", &a);
	return a;
}

int main() {
	int H, W; cin >> H >> W;
	vector<vint> S(H, vint(W));
	rep(i, H) {
		string s; cin >> s;
		rep(k, W) S[i][k] = s[k] - 'a';
	}
	int maxDepth = min(H, 6);
	vint rows(H);
	function<bool(int)> solve=[&](int depth) {
		if (depth < maxDepth) {
			rep(i, H) {
				bool ok = true;
				rep(k, depth) if (rows[k] == i) {
					ok = false;
					break;
				}
				if (ok) {
					rows[depth] = i;
					bool ret = solve(depth + 1);
					if (ret) return true;
				}
			}
			return false;
		}
		else {
			vint used(H);
			rep(i, depth) used[rows[i]] = 1;
			rep(i, H) if (! used[i]) rows[depth++] = i;
			
			// vint selfy(W);
			// rep(i, W) {
			// 	bool ok = true;
			// 	rep(k, H / 2) if (S[rows[k]][i] != S[rows[H - k]][i]) {
			// 		ok = false;
			// 		break;
			// 	}
			// 	if (ok) {
			// 		selfy[i] = true;
			// 	}
			// }
			
			vector<ll> ID(W), IDinv(W);
			map<ll, int> invs;
			rep(k, W) {
				rep(i, H) {
					ID[k] = ID[k] * 26 + S[rows[i]][k];
					IDinv[k] = IDinv[k] * 26 + S[rows[H - i - 1]][k];
				}
				invs[IDinv[k]]++;
			}
			rep(k, W) {
				if (ID[k] != IDinv[k]) {
					if (invs[ID[k]] == 0) {
						return false;
					}
					invs[ID[k]]--;
				}
				else {
					// int hoge = invs[ID[k]];
					//if ()
				}
			}
			
			int kyoyo = W % 2;
			for (auto &&i = invs.begin(); i != invs.end(); ++i) {
				if (i->second % 2 == 1) {
					if (kyoyo == 0) return false;
					kyoyo--;
				}
			}
			return true;
		}
	};
	cout << (solve(0) ? "YES" : "NO") << endl;
	return 0;
}


Measured memory_bytes: 256