#include <stdio.h>
#include <iostream>
#include <cstdio>
#include <algorithm> 
#include <stdio.h>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <list>
#include <utility>
#include <set>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <numeric>
#include <bitset>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <random>

using namespace std;

static const double EPS = 1e-9;
template<class T> bool INRANGE(T x, T a, T b) { return a <= x&&x <= b; }
template<class T> void amin(T &a, T v) { if (a > v) a = v; }
template<class T> void amax(T &a, T v) { if (a < v) a = v; }
int ROUND(double x) { return (int)(x + 0.5); }
bool ISINT(double x) { return fabs(ROUND(x) - x) <= EPS; }
bool ISEQUAL(double x, double y) { return fabs(x - y) <= EPS*max(1.0, max(fabs(x), fabs(y))); }
double SQSUM(double x, double y) { return x*x + y*y; }
#define PI  (acos(-1))
#define ARRAY_NUM(a) (sizeof(a)/sizeof(a[0])) 
#define NG (-1)
#define BIG ((int)1e9+10)
#define BIGLL ((ll)4e18)
#define SZ(a) ((int)(a).size())
#define SQ(a) ((a)*(a))
typedef unsigned long long ull;
typedef long long ll;


vector <bool> chosenRows;
vector <int> fixRows;

int H, W;
vector <string> vs;

bool canPair[12][12];
bool possible = false;


int getScore(const vector <int>& row, const vector <int>& col)
{
	int ret = 0;

	for (int y = 0; y < H; ++y)
	{
		for (int x = 0; x < W; x++)
		{
			if (vs[row[y]][col[x]] != vs[row[H-y-1]][col[W-x-1]])
			{
				ret--;
			}
		}
	}

	return ret;
}

class RandXor
{
public:
	RandXor()
	{
		init();
	}

	void init()
	{
		x = 123456789;
		y = 362436069;
		z = 521288629;
		w = 88675123;
	}

	void init(ll seed)
	{
		x = 123456789;
		y = static_cast<unsigned int>(362436069 + seed);
		z = static_cast<unsigned int>(521288629 - seed);
		w = static_cast<unsigned int>(88675123 * seed);
	}


	inline unsigned int rand()
	{
		unsigned int t;
		t = (x ^ (x << 11)); x = y; y = z; z = w; return(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
	}

	unsigned int next() {
		return rand();
	}

	unsigned int nextInt(int num) {
		return rand() % num;
	}

	unsigned char nextInt(unsigned char num) {
		return rand() % num;
	}

	// [a,b)
	int next(int a, int b) {
		return a + (rand() % (b - a));
	}

	// [-d,d]
	int nextDelta(int d) {
		return next(-d, d + 1);
	}

	double nextDouble() {
		return (rand() + 0.5) * (1.0 / 4294967296.0);
	}

	void randomShuffle(vector <int> &a) {
		const int n = SZ(a);
		for (int i = n - 1; i > 0; --i) {
			swap(a[i], a[nextInt(i + 1)]);
		}
	}

private:
	unsigned int x;
	unsigned int y;
	unsigned int z;
	unsigned int w;
};


int main()
{

	cin >> H >> W;

	for (int i = 0; i < H; ++i)
	{
		string s;
		cin >> s;
		vs.push_back(s);
	}

	vector <int> row(H);
	for (int i = 0; i < H; ++i)
	{
		row[i] = i;
	}

	vector <int> col(W);
	for (int i = 0; i < W; ++i)
	{
		col[i] = i;
	}

	RandXor randXor;

	int currentScore = getScore(row, col);

	double mStartTemp = 10.0;
	double mEndTemp = 1.0;

	const int T = 1000000;
	for (int i = 0; i < T; ++i)
	{
		const double progressRatio = static_cast<double>(i) / T;

		int type = randXor.nextInt(2);
		int a, b;
		if (type == 0)
		{
			a = randXor.nextInt(H);
			b = randXor.nextInt(H);
			swap(row[a], row[b]);
		}
		else
		{
			a = randXor.nextInt(W);
			b = randXor.nextInt(W);
			swap(col[a], col[b]);
		}

		int nextScore = getScore(row, col);

		const double deltaScore = nextScore - currentScore;

		bool isAccepted = deltaScore > 0;	// スコアが良くなった

											// 焼きなましのときはスコアが悪くなっても
											// 最初progressRatio=0.0のときは、スコアが良くなろうが悪くなろうが、常にnextを使用
											// 最後progressRatio=1.0のときは、スコアが改善したときのみ、nextを使用
		if (!isAccepted)
		{
			const double temp = mStartTemp + (mEndTemp - mStartTemp) * progressRatio;	// TODO : 等差等比mixなど、ありうる。
			const double left = exp(deltaScore / temp);

			const double r = randXor.nextDouble();
			isAccepted = left > r;	// スコアが悪くなっても強制遷移

									// fprintf(stderr, "temp=%f deltaScore=%f eft=%f r=%f ac=%d\n", temp, deltaScore, left, r, (int)isAccepted);
		}

		if (isAccepted)
		{
			currentScore = nextScore;
			// fprintf(stderr,"current Score=%.8f time=%lld\n", mCurrentScore, t);
		}
		else
		{
			if (type == 0)
			{
				swap(row[a], row[b]);
			}
			else
			{
				swap(col[a], col[b]);
			}
		}

		// ベストスコアは保存しておく。
		if (currentScore==0)
		{
			break;
		}
	}
	if (currentScore == 0)
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}

	return 0;
}

Measured memory_bytes: 384