#include <bits/stdc++.h>
#define debug(x) std::cerr << "Line: " << __LINE__ << \
                    "," << #x << "=" << x << "\n"
using ll = long long;
constexpr int N = 105;
int a[N];
// dp[上上行01状态][上行01状态][滚动]
int dp[1 << 10][1 << 10][3], sum[1 << 10];
int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n, m;
    std::cin >> n >> m;
    char x;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            std::cin >> x;
            if(x == 'H') a[i] |= (1 << j);//如果是山,那一位就是1
        }
    }
    for(int i = 0; i < (1 << m); i++){
        sum[i] = __builtin_popcount(i);//提前计算好对于每一个十进制,1的个数
    }
    //枚举第一行的每一种摆放状态
    for(int S = 0; S < (1 << m); S++){
        /*
            同时满足三个条件:
            1. 刚好和第一行地形"交错"放置
            2. 每个位置的前一格没有炮
            3. 每个位置的前两格没有炮
        */
        if(!(S & a[0] || S & (S << 1) || S & (S << 2) )){
            dp[0][S][0] = sum[S];//第0行什么都没有,第一行是枚举的初始化状态,滚动:0, 所放置的炮个数
        }
    }

    //枚举第一、二行的每一种摆放状态
    for(int L = 0; L < (1 << m); L++){//枚举上排状态 O(2 ^ m)
        for(int S = 0; S < (1 << m); S++){//枚举这排状态 O(2 ^ m)
            /*
                转移满足的条件:
                1.满足上行和这行不冲突
                2.满足上行和第一行不冲突
                3.满足这行和第二行不冲突
                4.满足上行与自己前一格、两格不冲突
                5.满足这行与自己前一格、两格不冲突
            */
            if(!(L & S || L & a[0] || S & a[1] || (L & (L<<1)) ||
                 (L & (L<<2)) || (S & (S<<1)) || (S & (S<<2))))    {
                //谜之一长串特判
                dp[L][S][1] = sum[S] + sum[L];//将前两行的所放置炮的个数加起来
            }
        }
    }

    //初始化完成,可以借以上两个初始化数据向后滚动递推
    for(int i = 2; i < n; i++){//对于之后的行
        for(int L = 0; L < (1 << m); L++){//这一行的上上行
            /*
                特判
                1.上行摆放状态与上行地形冲突
                2.自己de摆放状态和自己冲突
            */
            if(L & a[i - 1] || L & (L << 1) || L & (L << 2)){
                continue;
            }
            for(int S = 0; S < (1 << m); S++){
                /*
                    特判
                    1.这行摆放状态与这行地形冲突
                    2.自己de摆放状态和自己冲突
                */
                if(S & a[i] || L & S || S & (S << 1) || S & (S << 2)){
                    continue;
                }
                for(int FL = 0; FL < (1 << m); FL++){
                    /*
                        特判
                        1.上上行摆放状态与上上行地形冲突
                        2.上上自己de摆放状态和自己冲突
                    */
                    if(FL & L || FL & S || FL & a[i - 2] || FL & (FL << 1) || FL & (FL << 2)){
                        continue;
                    }
                    //滚动更新,这个状态要么和原来的一样,要么转移
                    dp[L][S][i % 3] = std::max(dp[L][S][i % 3], dp[FL][L][(i - 1) % 3] + sum[S]);
                }
            }
        }
    }
    int ans = -1e9;
    for(int L = 0;L < (1 << m);L ++)
        for(int S = 0; S < (1 << m); S++)
            ans = std::max(ans,dp[L][S][(n - 1) % 3]);    //结束状态可以是最后一行的任何状态
    std::cout << ans;
    return 0;
}