#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <math.h>
#include <iomanip>
#include <limits>
#include <list>
#include <queue>
#include <tuple>
#include <map>
#include <stack>
#include <set>
using namespace std;
#define MOD (long long int)(1e9+7)
#define ll long long int
#define rep(i,n) for(int i=0; i<(int)(n); i++)
#define reps(i,n) for(int i=1; i<=(int)(n); i++)
#define REP(i,n) for(int i=n-1; i>=0; i--)
#define REPS(i,n) for(int i=n; i>0; i--)
#define INF (int)(1123456789)
#define LINF (long long int)(112345678901234567)
#define chmax(a, b) a = (((a)<(b)) ? (b) : (a))
#define chmin(a, b) a = (((a)>(b)) ? (b) : (a))
#define all(v) v.begin(), v.end()

const int N = (int)3e5;

ll mpow(ll a, ll b){
	if(b==0) return 1;
	else if(b%2==0){ll memo = mpow(a,b/2); return memo*memo%MOD;}
	else return mpow(a,b-1) * a % MOD;
}
ll gcd(ll a, ll b){
	if(b==0) return a;
	else return gcd(b, a%b);
}
vector<ll> kaijo_memo;
ll kaijo(ll n){
	if(kaijo_memo.size() > n) return kaijo_memo[n];
	if(kaijo_memo.size() == 0) kaijo_memo.push_back(1);
	while(kaijo_memo.size() <= n) kaijo_memo.push_back(kaijo_memo[kaijo_memo.size()-1] * kaijo_memo.size() % MOD);
	return kaijo_memo[n];
}
ll nCr(ll n, ll r){
	if(n < r || r < 0) return 0;
	ll ret = 1;
	ret *= kaijo(n); ret %= MOD;
	ret *= mpow(kaijo(r), MOD-2); ret %= MOD;
	ret *= mpow(kaijo(n-r), MOD-2); ret %= MOD;
	return ret;
}

int h,w;
vector<string> s;

bool saiki(vector<int> x, vector<int> remain){
	if(remain.size() > 0){
		if(x.size() == 0 && remain.size()%2 == 1){
			rep(i, remain.size()){
				x.push_back(remain[i]);
				remain.erase(remain.begin() + i);
				if(saiki(x, remain)) return true;
				remain.insert(remain.begin() + i, x.back());
				x.pop_back();
			}
			return false;
		}else{
			x.push_back(remain[0]);
			remain.erase(remain.begin());
			rep(i,remain.size()){
				x.push_back(remain[i]);
				remain.erase(remain.begin() + i);
				if(saiki(x, remain)) return true;
				remain.insert(remain.begin() + i, x.back());
				x.pop_back();
			}
			return false;
		}
	}

	vector<string> groups;
	vector<int> plus, minus,total;
	if(x.size()%2 == 1){
		x.push_back(x[0]);
		x.erase(x.begin());
	}
	

	cerr<<"x:";
	rep(i,x.size()){
		cerr<<x[i]<<" ";
	}
	cerr<<endl;

	rep(y, h){
		string kouho = "";
		rep(j, (x.size()+1)/2*2){
			kouho.push_back(s[y][x[j]]);
			j++;
		}
		for(int j=x.size()/2*2-1; j>=0; j--){
			kouho.push_back(s[y][x[j]]);
			j--;
		}

		string kouho_rev = kouho;
		reverse(all(kouho_rev));
		cerr<<kouho_rev<<endl;
		rep(i, groups.size()+1){
			if(i == groups.size()){
				groups.push_back(kouho);
				plus.push_back(1);
				minus.push_back(0);
				break;
			}
			string t = groups[i];
			if(t == kouho){
				plus[i]++;
				break;
			}
			if(t == kouho_rev){
				minus[i]++;
				break;
			}
		}
	}
	rep(i,plus.size()){
		cerr<<"p:"<<plus[i]<<" m:"<<minus[i]<<endl;
	}
	int count = 0;
	rep(i, plus.size()){
		if(plus[i] != minus[i]){
			string rev = groups[i];
			reverse(all(rev));
			bool kaibun = (groups[i] == rev);
			if(kaibun){
				if((plus[i] + minus[i])%2 == 0){
					continue;
				}
			}else{
				return false;
			}
			if(h%2 == 0){
				return false;
			}
			if(count == 1){
				return false;
			}

			count++;
		}
	}

	return true;
}

int main(void){
	cin>>h>>w;
	rep(i,h){
		string inp;cin>>inp;
		s.push_back(inp);
	}
	vector<int> x,remain;
	rep(i,w){
		remain.push_back(i);
	}
	bool check = saiki(x, remain);
	cout<<(check?"YES":"NO")<<endl;
	return 0;
}


Measured memory_bytes: 256