using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("3 5");
WillReturn.Add("JOIJO");
WillReturn.Add("IJOOO");
WillReturn.Add("IIJIJ");
WillReturn.Add("JO");
WillReturn.Add("IJ");
//3
}
else if (InputPattern == "Input2") {
WillReturn.Add("2 6");
WillReturn.Add("JOJOJO");
WillReturn.Add("OJOJOJ");
WillReturn.Add("OJ");
WillReturn.Add("JO");
//2
}
else if (InputPattern == "Input3") {
WillReturn.Add("2 2");
WillReturn.Add("JI");
WillReturn.Add("IJ");
WillReturn.Add("JJ");
WillReturn.Add("JJ");
//0
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static char[,] mBanArr;
static char[,] mGoalArr;
static int UB_X;
static int UB_Y;
static void Main()
{
List<string> InputList = GetInputList();
int[] wkArr = InputList[0].Split(' ').Select(pX => int.Parse(pX)).ToArray();
int H = wkArr[0];
int W = wkArr[1];
mBanArr = CreateBanArr(InputList.Skip(1).Take(H));
mGoalArr = CreateBanArr(InputList.Skip(1 + H));
UB_X = mBanArr.GetUpperBound(0);
UB_Y = mBanArr.GetUpperBound(1);
// 初期状態での紋章の座標のハッシュ値の集合
var DefalstMonsyouSet = new HashSet<long>();
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
if (IsMonsyou(LoopX, LoopY)) {
DefalstMonsyouSet.Add(GetHash(LoopX, LoopY));
}
}
}
int DefaultCnt = DefalstMonsyouSet.Count;
var AnswerList = new List<int>();
AnswerList.Add(DefaultCnt);
var CharList = new List<char>() { 'J', 'O', 'I' };
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
char BeforeChar = mBanArr[LoopX, LoopY];
foreach (char EachAfterChar in CharList) {
if (EachAfterChar == BeforeChar) continue;
mBanArr[LoopX, LoopY] = EachAfterChar;
int AnswerCnt = DefaultCnt;
Action<int, int> CntAct = (pBaseX, pBaseY) =>
{
long Hash = GetHash(pBaseX, pBaseY);
if (DefalstMonsyouSet.Contains(Hash)) AnswerCnt--;
if (IsMonsyou(pBaseX, pBaseY)) {
AnswerCnt++;
}
};
CntAct(LoopX, LoopY);
CntAct(LoopX - 1, LoopY);
CntAct(LoopX, LoopY - 1);
CntAct(LoopX - 1, LoopY - 1);
AnswerList.Add(AnswerCnt);
mBanArr[LoopX, LoopY] = BeforeChar;
}
}
}
Console.WriteLine(AnswerList.Max());
}
static long GetHash(int pX, int pY)
{
long Hash = pX;
Hash *= 100000000;
Hash += pY;
return Hash;
}
// JOI紋章の左上候補の座標を引数とし、JOI紋章かを返す
static bool IsMonsyou(int pBaseX, int pBaseY)
{
if (pBaseX < 0 || UB_X < pBaseX) return false;
if (pBaseY < 0 || UB_Y < pBaseY) return false;
for (int LoopX = pBaseX; LoopX <= pBaseX + 1; LoopX++) {
if (UB_X < LoopX) return false;
for (int LoopY = pBaseY; LoopY <= pBaseY + 1; LoopY++) {
if (UB_Y < LoopY) return false;
int XVect = LoopX - pBaseX;
int YVect = LoopY - pBaseY;
if (mGoalArr[XVect, YVect] != mBanArr[LoopX, LoopY]) {
return false;
}
}
}
return true;
}
////////////////////////////////////////////////////////////////
// IEnumerable<string>をcharの2次元配列に設定する
////////////////////////////////////////////////////////////////
static char[,] CreateBanArr(IEnumerable<string> pStrEnum)
{
var StrList = pStrEnum.ToList();
if (StrList.Count == 0) {
return new char[0, 0];
}
int UB_X = StrList[0].Length - 1;
int UB_Y = StrList.Count - 1;
char[,] WillReturn = new char[UB_X + 1, UB_Y + 1];
for (int Y = 0; Y <= UB_Y; Y++) {
for (int X = 0; X <= UB_X; X++) {
WillReturn[X, Y] = StrList[Y][X];
}
}
return WillReturn;
}
}