#define _CRT_SECURE_NO_WARNINGS


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <iomanip>
#include <map>
#include <stack>
#include <bitset>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <random>
#include <cassert>

using namespace std;

//#define FAST_ALLOCATOR_MEMORY 1e8
#undef LOCAL
//#include "optimization.h"

bool ans = 0;
int order[12];
bool used[12];
int h, w;
bool bad;
vector<string> a(12);

void doit(int lvl, int pos) {
	if (ans)
		return;
	if (lvl == (w + 1) / 2) {
		int l = 0;
		vector<int> p(w);
		for (int i = 0; i < 12; i++) {
			if (order[i] == i)
				p[((w + 1) / 2) - 1] = i;
			if (order[i] > i) {
				p[l] = i;
				p[w - 1 - l] = order[i];
				l++;
			}
		}
		multiset<string> s;
		for (int i = 0; i < h; i++) {
			string nw = a[i];
			for (int j = 0; j < w; j++)
				nw[j] = a[i][p[j]];
			if (s.find(nw) != s.end())
				s.erase(s.find(nw));
			else {
				reverse(nw.begin(), nw.end());
				s.insert(nw);
			}
		}
		if (s.size() <= 1)
			ans = 1;
		return;
	}
	if (used[pos]) {
		doit(lvl, pos + 1);
		return;
	}
	if (bad) {
		order[pos] = pos;
		bad = 0;
		doit(lvl + 1, pos + 1);
		bad = 1;
	}
	for (int i = pos + 1; i < w; i++) {
		if (!used[i]) {
			used[i] = 1, used[pos] = 1;
			order[i] = pos, order[pos] = i;
			doit(lvl + 1, pos + 1);
			used[i] = 0, used[pos] = 0;
		}
	}

}

int main() {
	iostream::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

#ifdef LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	cin >> h >> w;
	for (int i = 0; i < h; i++)
		cin >> a[i];
	bad = w % 2 == 1;
	doit(0, 0);
	if (ans)
		cout << "YES";
	else
		cout << "NO";
	return 0;
}