#include <bits/stdc++.h>



#define all(x) (x).begin(), (x).end()
#define make_unique(x) sort((x).begin(), (x).end()); (x).erase(unique((x).begin(), (x).end()), (x).end())

using namespace std;

using li = long long;
using LI = __int128_t;
using ld = long double;
using ull = unsigned long long;

LI gcd(LI x, LI y) {
	while (y) {
		x %= y;
		swap(x, y);
	}
	return x;
}

LI lcm(LI x, LI y) {
	return x / gcd(x, y) * y;
}

ostream& operator <<(ostream& ostr, LI x) {
	static constexpr li BIG = 1e18;
	if (x < 0) {
		ostr << "-";
		x = -x;
	}
	if (x < BIG) {
		return ostr << (li)x;
	} else {
		stringstream ss;
		ss << setfill('0') << setw(18) << (li)(x % BIG);
		return ostr << (li)(x / BIG) << ss.str();
	}
}

template <typename T>
ostream& operator <<(ostream& ostr, const vector<T>& vec) {
	auto [pre, sep, post] = (&ostr == &cout) ? make_tuple("", " ", "") : make_tuple("[", ", ", "]");
	ostr << pre;
	bool fp = true;
	for (const auto& x : vec) {
		if (fp) {
			fp = false;
		} else {
			ostr << sep;
		}
		ostr << x;
	}
	return ostr << post;
}

template <typename T>
ostream& operator <<(ostream& ostr, const set<T>& st) {
	auto [pre, sep, post] = (&ostr == &cout) ? make_tuple("", " ", "") : make_tuple("{", ", ", "}");
	ostr << pre;
	bool fp = true;
	for (const auto& x : st) {
		if (fp) {
			fp = false;
		} else {
			ostr << sep;
		}
		ostr << x;
	}
	return ostr << post;
}

template <typename T>
ostream& operator <<(ostream& ostr, const multiset<T>& st) {
	auto [pre, sep, post] = (&ostr == &cout) ? make_tuple("", " ", "") : make_tuple("{", ", ", "}");
	ostr << pre;
	bool fp = true;
	for (const auto& x : st) {
		if (fp) {
			fp = false;
		} else {
			ostr << sep;
		}
		ostr << x;
	}
	return ostr << post;
}

template <typename T, typename U>
ostream& operator <<(ostream& ostr, const map<T, U>& mp) {
	ostr << "{";
	bool fp = true;
	for (const auto& [k, v] : mp) {
		if (fp) {
			fp = false;
		} else {
			ostr << ", ";
		}
		ostr << k << ": " << v;
	}
	return ostr << "}";
}

template <typename T, typename U>
ostream& operator <<(ostream& ostr, const pair<T, U>& p) {
	return ostr << "(" << p.first << ", " << p.second << ")";
}

#define imie(x) #x << ": " << x

template <typename T>
istream& operator >>(istream& istr, vector<T>& vec) {
	for (auto& x : vec) {
		istr >> x;
	}
	return istr;
}

template <typename T = int>
T nxt(istream& istr = cin) {
	T x;
	istr >> x;
	return x;
}

template <typename T>
bool is_zero(const T& x) {
	if (is_integral_v<T>) {
		return x == 0;
	} else if (is_floating_point_v<T>) {
		return abs(x) < 1e-8;
	} else {
		assert(false);
	}
}
template <typename int_type = int>
class Dinic {
public:
	struct Edge {
		int from, to;
		int_type cap, flow;
	};

	int n;
	vector<Edge> edges;
	vector<vector<int>> eids;

	Dinic(int _n): n(_n), eids(_n) {}

	void add_edge(int u, int v, int_type c) {
		eids[u].push_back(edges.size());
		edges.push_back({u, v, c, 0});
		eids[v].push_back(edges.size());
		edges.push_back({v, u, 0, 0});
	}

	int_type k_flow(int s, int t, int_type flow_limit) {
		int_type ans = 0;
		while (bfs(s, t)) {
			ptr.assign(n, 0);
			while (int_type new_flow = push_flow(s, t, flow_limit)) {
				ans += new_flow;
				flow_limit -= new_flow;
			}
			if (!flow_limit) {
				break;
			}
		}
		return ans;
	}

	int_type max_flow(int s, int t) {
		return k_flow(s, t, inf);
	}

private:
	vector<int> d;
	vector<int> ptr;
	static constexpr int_type inf = numeric_limits<int_type>::max();

	int_type push_flow(int s, int t, int_type flow_limit) {
		if (!flow_limit) {
			return 0;
		}
		if (s == t) {
			return flow_limit;
		}
		int_type res = 0;
		for (int& i = ptr[s]; i < (int)eids[s].size(); ++i) {
			if (!flow_limit) {
				break;
			}
			int eid = eids[s][i];
			if (d[edges[eid].to] != d[s] + 1 || edges[eid].flow == edges[eid].cap) {
				continue;
			}
			int_type added_flow = push_flow(edges[eid].to, t, min(edges[eid].cap - edges[eid].flow, flow_limit));
			if (!added_flow) {
				continue;
			}
			edges[eid].flow += added_flow;
			edges[eid ^ 1].flow -= added_flow;
			flow_limit -= added_flow;
			res += added_flow;
		}
		return res;
	}

	bool bfs(int s, int t) {
		d.assign(n, inf);
		d[s] = 0;
		queue<int> q;
		q.push(s);
		while (!q.empty()) {
			int v = q.front();
			q.pop();
			for (int eid : eids[v]) {
				if (edges[eid].flow == edges[eid].cap) {
					continue;
				}
				int to = edges[eid].to;
				if (d[to] > d[v] + 1) {
					d[to] = d[v] + 1;
					q.push(to);
				}
			}
		}
		return d[t] != inf;
	}
};

int main() {
	int n = nxt(), m = nxt();
	Dinic<int> net(n * m + 2);
	vector<string> a(n);
	for (int i = 0; i < n; ++i) {
		cin >> a[i];
		for (int j = 0; j < m; ++j) {
			if (a[i][j] == '.') {
				if (i % 2 == j % 2) {
					net.add_edge(n * m, i * m + j, 1);
				} else {
					net.add_edge(i * m + j, n * m + 1, 1);
				}
				if (i && a[i - 1][j] == '.') {
					if (i % 2 == j % 2) {
						net.add_edge(i * m + j, (i - 1) * m + j, 1);
					} else {
						net.add_edge((i - 1) * m + j, i * m + j, 1);
					}
				}
				if (j && a[i][j - 1] == '.') {
					if (i % 2 == j % 2) {
						net.add_edge(i * m + j, i * m + (j - 1), 1);
					} else {
						net.add_edge(i * m + (j - 1), i * m + j, 1);
					}
				}
			}
		}
	}
	cout << net.max_flow(n * m, n * m + 1) << "\n";
	for (const auto& e : net.edges) {
		if (e.from < n * m && e.to < n * m && e.flow == 1) {
			int i = e.from, j = e.to;
			assert(a[i / m][i % m] == '.');
			assert(a[j / m][j % m] == '.');
			if (i > j) {
				swap(i, j);
			}
			assert(i + 1 == j || i + m == j);
			if (i + m == j) {
				a[i / m][i % m] = 'v';
				a[j / m][j % m] = '^';
			} else {
				a[i / m][i % m] = '>';
				a[j / m][j % m] = '<';
			}
		}
	}
	for (auto s : a) {
		cout << s << "\n";
	}

	return 0;
}



Measured memory_bytes: 4792