#include <iostream>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <vector>
#include <valarray>
#include <array>
#include <queue>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <complex>
#include <random>

using namespace std;
using ll = long long;
using ull = unsigned long long;

/**
 * hash???????????????UnionFind ????´???°???16????????????????????¨??????????????????
 */
template<int N>
struct SUnionFind {
    int i2g[N];
    SUnionFind() {
        for (int i = 0; i < N; i++) {
            i2g[i] = i;
        }
    }
    SUnionFind(ull hs) {
        for (int i = 0; i < N; i++) {
            i2g[i] = hs & 0xf;
            hs >>= 4;
        }
    }

    void merge(int x, int y) {
        int xg = i2g[x], yg = i2g[y];
        for (int i = 0; i < N; i++) {
            if (i2g[i] == yg) i2g[i] = xg;
        }
    }

    bool same(int x, int y) {
        return i2g[x] == i2g[y];
    }

    ull uf2hash() {
        int b[N];
        memset(b, -1, sizeof(b));
        int c = 0;
        ull hs = 0;
        for (int i = N-1; i >= 0; i--) {
            hs <<= 4;
            if (b[i2g[i]] == -1) {
                b[i2g[i]] = c;
                c++;
            }
            hs += b[i2g[i]];
        }
        return hs;
    }
};


int main() {
    int H, W;
    cin >> H >> W;
    if (H == 1 && W == 1) {
        char c;
        cin >> c;
        if (c == '.') {
            cout << 0 << endl;
        } else {
            cout << 1 << endl;
        }
        return 0;
    }
    bool te = (H > W);
    int isB[11] = {}, isW[11] = {};
    for (int i = 0; i < H; i++) {
        string s;
        cin >> s;
        for (int j = 0; j < W; j++) {
            if (s[j] == '#') {
                if (!te) {
                    isB[i] |= 1<<j;
                } else {
                    isB[j] |= 1<<i;
                }
            }
            if (s[j] == '.') {
                if (!te) {
                    isW[i] |= 1<<j;
                } else {
                    isW[j] |= 1<<i;
                }
            }
        }
    }
    if (te) swap(H, W);
    int mW = (1<<W)-1;
    map<ull, int> mp;
    {
        SUnionFind<16> uf;
        for (int i = 0; i < W-1; i++) {
            uf.merge(i, i+1);
        }

        mp[uf.uf2hash()] = 0;
    }

    for (int h = 0; h < H; h++) {
        map<ull, int> mp2;
        for (auto p: mp) {
            SUnionFind<16> uf(p.first);
            for (int nmp = 0; nmp < (1<<W); nmp++) {
                if (nmp & isW[h]) continue;
                if ((mW^nmp) & isB[h]) continue;
                SUnionFind<16> nf;
                bool cf = false;
                {
                    //?????£?¶?
                    for (int i = 0; i < W-1; i++) {
                        if ((mW^nmp) & (1<<i) and (mW^nmp) & (2<<i)) {
                            cf = true;
                            break;
                        }
                    }
                    if (cf) continue;
                    for (int i = 0; i < W; i++) {
                        if ((mW^nmp) & (1<<i)) {
                            if (uf.same(i, W)) {
                                cf = true;
                                break;
                            }
                            nf.merge(i, W);
                        }
                    }
                    if (cf) continue;
                }
                if (h) {
                    for (int i = 0; i < W; i++) {
                        if (!(nmp & (1<<i))) continue;
                        if (uf.same(i, W)) continue;
                        for (int j = i+1; j < W; j++) {
                            if (!(nmp & (1<<j))) continue;
                            if (uf.same(j, W)) continue;
                            if (uf.same(i, j)) {
                                nf.merge(i, j);
                            }
                        }
                    }
                }
                {
                    for (int i = 0; i < W-1; i++) {
                        if (!(nmp & (1<<i))) continue;
                        if (!(nmp & (2<<i))) continue;
                        if (nf.same(i, i+1)) {
                            cf = true;
                            break;
                        }
                        nf.merge(i, i+1);
                    }
                    if (cf) continue;
                }
                bool con[16] = {};
                for (int i = 0; i < W; i++) {
                    if (uf.same(i, W)) continue;
                    if (nf.same(i, W)) continue;
                    con[uf.i2g[i]] = true;
                }
                for (int i = 0; i < W; i++) {
                    if (uf.same(i, W)) continue;
                    if (!nf.same(i, W)) continue;
                    if (!con[uf.i2g[i]]) {
                        cf = true;
                        break;
                    }
                }
                if (cf) continue;
                ull res = nf.uf2hash();
                if (mp2.count(res)) {
                    mp2[res] = max(mp2[res], p.second + __builtin_popcount(nmp));
                } else {
                    mp2[res] = p.second + __builtin_popcount(nmp);
                }
            }
        }
        mp = mp2;
//        printf("end %ld\n", mp.size());
    }

    int res = -1;
    for (auto p: mp) {
        SUnionFind<16> uf(p.first);
        bool cf = false;
        int fb = -1;
        for (int i = 0; i < W; i++) {
            if (!uf.same(i, W)) {
                if (fb == -1) {
                    fb = i;
                    continue;
                }
                if (!uf.same(fb, i)) {
                    cf = true;
                    break;
                }
            }
        }
        if (cf) {
            continue;
        }
        res = max(res, p.second);
    }
    cout << res << endl;
    return 0;
}

Measured memory_bytes: 3464