#include "omp.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <vector>	
#include <cassert>
#include <fstream>
#include <algorithm>
#include <list>
#include <random>
#include <ctime>
#include <random>
#include <bitset>
#include <unordered_set>
#include <cassert>
#include <list>


using namespace std;

typedef long long ll;
typedef long double ld;

const int sze = 12;




int n, m, rej;
vector<int> gt;
vector<string> mp;


void get(int num, int cur, int mod, int N)
{
	if (cur == rej + 1)
		return;

	if (!mod)
	{
		gt.push_back(num);
		return;
	}

	if (cur >= N)
		return;

	for (int i = cur; i < N; ++i)
		get(num | (1 << i), i + 1, mod - 1, N);
}


string solution1()
{
	if (n & 1)
	{
		for (int mid = 0; mid < n; ++mid)
		{
			vector<string> cur = mp;
			cur[n / 2] = mp[mid];
			rej = mid;

			gt.clear();
			get(0, 0, n / 2, n);

			for (int a : gt)
			{
				vector<int> up, down;

				for (int i = 0; i < n; ++i)
					if (a & (1 << i))
						up.push_back(i);
					else if (i != mid)
						down.push_back(i);

				do
				{
					for (int i = 0; i < n / 2; ++i)
						cur[i] = mp[up[i]];

					for (int i = 0; i < n / 2; ++i)
						cur[i + n / 2 + 1] = mp[down[i]];

					vector<int> used(m);
					for (int i = 0; i < m; ++i)
						for (int j = i + 1; j < m; ++j)
						{
							if (used[i] || used[j])
								continue;

							int cr = 0;
							while (cr < n && cur[cr][i] == cur[n - cr - 1][j])
								cr++;

							if (cr == n)
								used[i] = used[j] = 1;
						}

					if (count(used.begin(), used.end(), 0) == 0)
						return "YES";


					if (count(used.begin(), used.end(), 0) == 1)
						for (int i = 0; i < m; ++i)
							if (!used[i])
							{
								int j = i;

								int cr = 0;
								while (cr < n && cur[cr][i] == cur[n - cr - 1][j])
									cr++;

								if (cr == n)
									return "YES";
							}

				} while (next_permutation(down.begin(), down.end()));
			}
		}
	}
	else
	{
		vector<string> cur = mp;

		rej = -10;
		gt.clear();
		get(0, 0, n / 2, n);

		for (int a : gt)
		{
			vector<int> up, down;

			for (int i = 0; i < n; ++i)
				if (a & (1 << i))
					up.push_back(i);
				else
					down.push_back(i);

			do
			{				
				for (int i = 0; i < n / 2; ++i)
					cur[i] = mp[up[i]];

				for (int i = 0; i < n / 2; ++i)
					cur[i + n / 2] = mp[down[i]];

				vector<int> used(m);
				for (int i = 0; i < m; ++i)
					for (int j = i + 1; j < m; ++j)
					{
						if (used[i] || used[j])
							continue;

						int cr = 0;
						while (cr < n && cur[cr][i] == cur[n - cr - 1][j])
							cr++;

						if (cr == n)
							used[i] = used[j] = 1;
					}

				if (count(used.begin(), used.end(), 0) == 0)
					return "YES";


				if (count(used.begin(), used.end(), 0) == 1)
					for (int i = 0; i < m; ++i)
						if (!used[i])
						{
							int j = i;

							int cr = 0;
							while (cr < n && cur[cr][i] == cur[n - cr - 1][j])
								cr++;

							if (cr == n)
								return "YES";
						}

			} while (next_permutation(down.begin(), down.end()));
		}
	}


	return "NO";
}


bool equal(vector<string> tr)
{
	bool label = true;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < m; ++j)
			label = min(label, tr[i][j] == tr[n - 1 - i][m - 1 - j]);

	return label;
}


string stupid_solution()
{
	vector<int> strings(n), columns(m);
	for (int i = 0; i < n; ++i)
		strings[i] = i;

	do
	{
		for (int j = 0; j < m; ++j)
			columns[j] = j;

		do
		{
			vector<string> tr = mp;

			for (int i = 0; i < n; ++i)
				for (int j = 0; j < m; ++j)
					tr[i][j] = mp[strings[i]][columns[j]];

			if (equal(tr))
				return "YES";

		} while (next_permutation(columns.begin(), columns.end()));
	} while (next_permutation(strings.begin(), strings.end()));

	return "NO";
}



signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> n >> m;
	mp.resize(n);

	for (int i = 0; i < n; ++i)
		cin >> mp[i];

	cout << solution1();

	return 0;
}


Measured memory_bytes: 256