typedef long long ll;
using namespace std;
const int N = 5e2 + 10;
const int mod = 1e9 + 7;
int dp[1010][1010]; //dp[i][j]代表前i列,第i列的状态压缩为j的合法方案数
int n,m;
string a[4];
int p3[5] = {1,3,9,27,81}; //状态压缩,我们把red用四位三进制数表示
map<char,int> mp;
int b[4] = {}; //用来存出状态为j的三进制代码
int check(int x){
for (int i = 0; i < n; ++i) {
b[i] = x%3;
x /= 3;
}
for (int i = 1; i < n; ++i) {
if(b[i] == b[i-1]) return 0; //该状态存在相邻元素相等的情况
}
return 1;
}
//重写check函数
int check(int x,int y){
for (int i = 0; i < n; ++i) {
if(x%3 == y%3) return 0;
x /= 3;
y /= 3;
}
return 1;
}
void solve(){
cin>>n>>m;
for (int i = 0; i <= n; ++i) {
cin>>a[i];
}
memset(dp,0x3f,sizeof(dp));
mp['r'] = 0;
mp['e'] = 1;
mp['d'] = 2;
int i;
ll ans = 1e9;
for (i = 0; i < m; ++i) { //枚举每一列的情况
if(i == 0){ //如果为第一列,就只需要考虑这一列元素是否会出现相邻元素相等的情况
for (int j = 0; j < p3[n]; ++j) { //j转换为三进制就代表每一种情况,j最大为81
if(check(j)){
int cnt = 0;
for (int k = 0; k < n; ++k) {
if(b[k] != mp[a[k][i]]){
cnt++; //如果该状态的第k行与该数组不相同,就代表需要修改,cnt++
}
}
dp[0][j] = min(dp[0][j],cnt);
}
}
} else{ //其它列的情况
for (int j = 0; j < p3[n]; ++j) { //j转换为三进制就代表每一种情况,j最大为81
if(check(j)){
int cnt = 0;
for (int k = 0; k < n; ++k) {
if(b[k] != mp[a[k][i]]){
cnt++; //如果该状态的第k行与该数组不相同,就代表需要修改,cnt++
}
}
//不经需要判断这一列是否满足,还需要判断与前一列是否满足要求
for (int k = 0; k < p3[n]; ++k) {
if(check(j,k)){
dp[i][j] = min(dp[i][j],dp[i-1][k] + cnt);
}
}
}
}
}
if(i == m-1){
for (int j = 0; j < p3[n]; ++j) {
ans = min(ans, (ll) dp[i][j]);
}
}
}
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin>>t;
while(t--){
solve();
}
return 0 ;
}