#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<vector>
#include<array>
#include<string>
#include<stack>
#include<queue>
#include<algorithm>
#include<cassert>
#include<functional>
#include<random>
#include<complex>
//#include<boost/multiprecision/cpp_int.hpp>
#define int int64_t
#define REP(i, a, b) for (int64_t i = (int64_t)(a); i < (int64_t)(b); i++)
#define rep(i, a) REP(i, 0, a)
#define EACH(i, a) for (auto i: a)
#define ITR(x, a) for (auto x = a.begin(); x != a.end(); x++)
#define ALL(a) (a.begin()), (a.end())
#define HAS(a, x) (a.find(x) != a.end())
#define Min(x) *min_element(ALL(x))
#define Max(x) *max_element(ALL(x))
#define Unique(L) (L.erase(unique(ALL(L)), L.end()))
using namespace std;
//typedef boost::multiprecision::cpp_int bigint;
const double EPS = 1e-9;
const double PI = acos(-1.0);

template <class Flow>class MaxFlow {
private:

	struct Edge {
		int to;
		Flow cap;
		int rev;
		Edge(int to, Flow cap, int rev) : to(to), cap(cap), rev(rev) {}
	};

	Flow INF;
	Flow EPS;
	int size;
	vector<int>level;
	vector<int>iter;
	vector<int>que;
	vector<vector<Edge>>graph;
public:

	MaxFlow(const int n, Flow eps = 0, Flow inf = 1e9) :
		level(n), iter(n), que(n), size(n), graph(n), EPS(eps), INF(inf) {}

	void add_edge(const int from, const int to, const Flow cap) {
		graph[from].push_back(Edge(to, cap, graph[to].size()));
		graph[to].push_back(Edge(from, 0, graph[from].size() - 1));
	}

	void add_undirected_edge(const int from, const int to, const Flow cap) {
		graph[from].push_back(Edge(to, cap, graph[to].size()));
		graph[to].push_back(Edge(from, cap, graph[from].size() - 1));
	}

	Flow max_flow(const int source, const int sink) {
		Flow flow = 0;

		while (1) {
			bfs(source, sink);

			if (level[sink] == -1) return flow;

			for (int i = 0; i < size; i++) iter[i] = 0;

			flow += dfs(source, sink, INF);
		}
	}

private:

	void bfs(const int from, const int to) {
		int qs = 0, qt = 0;

		for (int i = 0; i < size; i++) level[i] = -1;

		level[from] = 0;
		que[qt++] = from;

		while (qs < qt && level[to] == -1) {
			int now = que[qs++];

			for (int i = 0; i < graph[now].size(); i++) {
				int next = graph[now][i].to;

				if (graph[now][i].cap > EPS && level[next] == -1) {
					level[next] = level[now] + 1;
					que[qt++] = next;
				}
			}
		}
	}

	Flow dfs(const int from, const int to, const Flow cap) {
		Flow flow = 0;

		if (from == to || cap <= EPS) return cap;

		for (int &i = iter[to]; i < graph[to].size(); i++) {
			int next = graph[to][i].to;
			Edge &edge = graph[next][graph[to][i].rev];
			Flow res;

			if (edge.cap <= EPS || level[next] >= level[to]) continue;

			res = dfs(from, next, min(cap - flow, edge.cap));

			if (res <= EPS) continue;

			edge.cap -= res;
			graph[to][i].cap += res;
			flow += res;

			if (abs(flow - cap) <= EPS) break;
		}

		return flow;
	}
};

signed main(void) {
	cin.tie(0);
	ios::sync_with_stdio(false);

	int H, W;
	cin >> H >> W;
	MaxFlow<int>g(H * W * 2 + 2);
	const int source = H * W * 2, sink = source + 1;
	const int D[5] = { 0,1,0,-1,0 };
	const int INF = 99999999;

	//柵は辺を塞ぐのではなくマス目を塞ぐ。最小カット問題に落とし込むために、
	//グリッドを仮想的に表と裏の2つ用意する。
	//(1)ソース→ヤギのいる表座標、
	//(2)任意の表座標→その4近傍の裏座標、
	//(3)端っこの表座標→シンク
	//の3要素をキャパ無限でつなぎ、
	//(4)任意の裏座標→表の同じ座標 をキャパ1でつなぐ。
	//裏から表へのパスをカットすることが"柵を置く"ことに等しい。
	rep(i, H) {
		string S;
		cin >> S;
		rep(j, W) {
			const int pos = i * W + j;
			if (S[j] == 'X') {
				if (i == 0 || i == H - 1 || j == 0 || j == W - 1) {
					cout << -1 << endl;
					return 0;
				}
				g.add_edge(source, pos, INF);//(1)

			}
			else {
				if (i == 0 || i == H - 1 || j == 0 || j == W - 1) {
					g.add_edge(pos, sink, INF);//(3)
				}
			}
			g.add_edge(pos + H * W, pos, 1);//(4)
			rep(k, 4) {
				const int di = i + D[k];
				const int dj = j + D[k + 1];
				if (!(0 <= di && di < H && 0 <= dj && dj < W))continue;
				g.add_edge(pos, di * W + dj + H * W, INF);//(2)
			}
		}
	}

	cout << g.max_flow(source, sink) << endl;
}

Measured memory_bytes: 4992