Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

JOI 紋章(JOI Emblem)

情報オリンピック日本委員会では，台湾大会に向かう選手を応援するために新しいJOI 旗を作成することにした．

JOI 旗は，正方形が縦に $M$ 行，横に $N$ 列並んだ形をしており，各正方形には J，O，I のいずれかの文字が1 つずつ書かれている．

情報オリンピック日本委員会はJOI 旗とは別にJOI 紋章というものを決めている．JOI 紋章は，正方形が縦に2 行，横に2 列並んだ形をしており，各正方形にはJ，O，I のいずれかの文字が1 つずつ書かれている．

JOI 旗に含まれるJOI 紋章の個数とは，JOI 旗に含まれる縦2 行，横2 列の領域のうち，その領域のJ，O，I の配置がJOI 紋章と(回転や裏返しをせずに) 一致しているものの個数のことである．条件を満たす縦2 行，横2 列の領域同士が重なっていてもそれらを別々に数えるものとする．

情報オリンピック日本委員会は古いJOI 旗と1 枚の白紙を持っている．白紙はJOI 旗を構成する正方形1 個分の大きさで，J，O，I のうち好きな1 文字を書き込むことができる．情報オリンピック日本委員会は以下のいずれか1 つの操作をして，新しいJOI 旗を作成することにした．

古いJOI 旗に対して何も操作せず，そのまま新しいJOI 旗とする．白紙は使用しない．

白紙に1 文字書き込み，古いJOI 旗のいずれかの正方形に重ねて貼り付けることで古いJOI 旗のうち1 箇所を変更する．変更後のJOI 旗を新しいJOI 旗とする．

情報オリンピック日本委員会は新しいJOI 旗に含まれるJOI 紋章の個数をできるだけ多くしたいと思っている．あなたは新しいJOI 旗に含まれるJOI 紋章の個数の最大値を求めることになった．

課題

古いJOI 旗とJOI 紋章の情報が与えられたとき，新しいJOI 旗に含まれるJOI 紋章の個数の最大値を求めるプログラムを作成せよ．

入力

標準入力から以下のデータを読み込め．

1 行目には2 個の整数 $M, N$ が空白を区切りとして書かれている．これはJOI 旗が正方形が縦に$M$行，横に$N$ 列並んだ形であることを表している．

続く$M$ 行の各行には，それぞれ$N$ 文字からなる文字列が書かれている．各文字はJ，O，I のいずれかであり，$M$ 行のうち上から$i$ 行目$(1 \leq i \leq M)$ に書かれている文字列の左から$j$ 文字目$(1 \leq j \leq N)$は古いJOI 旗の上から$i$ 行目，左から$j$ 列目の正方形に書かれている文字を表す．

続く2 行の各行には，それぞれ2 文字からなる文字列が書かれている．各文字はJ，O，I のいずれかであり，2 行のうち上から$i$ 行目$(1 \leq i \leq 2)$ に書かれている文字列の左から$j$ 文字目$(1 \leq j \leq 2)$ はJOI 紋章の上から$i$ 行目，左から$j$ 列目の正方形に書かれている文字を表す．

出力

標準出力に，新しいJOI 旗に含まれるJOI 紋章の個数の最大値を表す整数を1 行で出力せよ．

制約

すべての入力データは以下の条件を満たす．

$2 \leq M \leq 1000$

$2 \leq N \leq 1000$

入出力例

入力例 1

3 5
JOIJO
IJOOO
IIJIJ
JO
IJ

出力例 1

3

古いJOI 旗とJOI 紋章は問題文中の例の通りである．上から2 行目，左から4 列目の正方形を白紙を用いてJ に変更すると，次のような形になる．

このように変更した後のJOI 旗には次に示す3 箇所にJOI 紋章と同じ配置の領域がある．

JOI 紋章と同じ配置の領域が4 箇所以上となる新しいJOI 旗は存在しないので，新しいJOI 旗に含まれるJOI 紋章の個数の最大値は3 である．

入力例 2

2 6
JOJOJO
OJOJOJ
OJ
JO

出力例 2

2

白紙を使用しなくても最大値が得られる場合があることに注意せよ．

入力例 3

2 2
JI
IJ
JJ
JJ

出力例 3

0

この入出力例の場合，考えられるどの新しいJOI 旗においても，JOI 紋章が1 つも含まれない．

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in JAVA (judge verdict: Accepted)

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Map;
import java.util.PriorityQueue;
import java.math.BigInteger;
    
public class Main{

static final InputStream in=System.in;
static final PrintWriter out=new PrintWriter(System.out);
static final int INF=Integer.MAX_VALUE/2;
static final long LINF=Long.MAX_VALUE/2;
static int m,n;
static char[][] flag;
static char[][] emblem;
static final char[] JOI={'J','O','I'};
    
public static void main(String[] args) throws IOException{
InputReader ir=new InputReader(in);
m=ir.nextInt();
n=ir.nextInt();
flag=ir.makeCharMap(m,n);
emblem=ir.makeCharMap(2,2);
int sum=0;
int[][] count=new int[m][n];
for(int i=0;i<m;i++) for(int j=0;j<n;j++)count[i][j]=_count(i,j);
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
int t=0;
if(i!=0&&j!=0) t+=count[i-1][j-1];
if(i!=0) t+=count[i-1][j];
if(j!=0) t+=count[i][j-1];
t+=count[i][j];
sum+=t;
}
}
int ans=sum;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
for(int k=0;k<3;k++){
if(flag[i][j]!=JOI[k]){
char c=flag[i][j];
int t=sum;
if(i!=0&&j!=0) t-=4*count[i-1][j-1];
if(i!=0) t-=4*count[i-1][j];
if(j!=0) t-=4*count[i][j-1];
t-=4*count[i][j];
flag[i][j]=JOI[k];
t+=4*_count(i-1,j-1)+4*_count(i-1,j)+4*_count(i,j-1)+4*_count(i,j);
ans=Math.max(t,ans);
flag[i][j]=c;
}
}
}
}
out.println(ans/4);
out.flush();
}

public static int _count(int x,int y){
if(x<0||y<0||x>=m-1||y>=n-1) return 0;
if(flag[x][y]==emblem[0][0]&&flag[x+1][y]==emblem[1][0]&&flag[x][y+1]==emblem[0][1]&&flag[x+1][y+1]==emblem[1][1]) return 1;
return 0;
} 

static class InputReader {
private InputStream in;
private byte[] buffer=new byte[1024];
private int curbuf;
private int lenbuf;

public InputReader(InputStream in) {this.in=in;}
  
public int readByte() {
if(lenbuf==-1) throw new InputMismatchException();
if(curbuf>=lenbuf){
curbuf= 0;
try{
lenbuf=in.read(buffer);
}
catch (IOException e) {
throw new InputMismatchException();
}
if(lenbuf<=0)
return -1;
}
return buffer[curbuf++];
}

public boolean isSpaceChar(int c){return !(c>=33&&c<=126);}

private int skip() {int b; while((b = readByte())!=-1&&isSpaceChar(b)); return b;}

public String next() {
int b=skip();
StringBuilder sb=new StringBuilder();
while(!isSpaceChar(b)){
sb.appendCodePoint(b);
b=readByte();
}
return sb.toString();
}
 
public int nextInt() {
int c=readByte();
while (isSpaceChar(c)) c=readByte();
boolean minus=false;
if (c=='-') {
minus=true;
c=readByte();
}
int res=0;
do{
if(c<'0'||c>'9') throw new InputMismatchException();
res=res*10+c-'0';
c=readByte();
}while(!isSpaceChar(c));
return (minus)?-res:res;
}

public long nextLong() {
int c=readByte();
while (isSpaceChar(c)) c=readByte();
boolean minus=false;
if (c=='-') {
minus=true;
c=readByte();
}
long res = 0;
do{
if(c<'0'||c>'9') throw new InputMismatchException();
res=res*10+c-'0';
c=readByte();
}while(!isSpaceChar(c));
return (minus)?-res:res;
}

public int[] toIntArray(int n){
int[] a=new int[n];
for(int i=0;i<n;i++) a[i]=nextInt();
return a;
}

public char[][] makeCharMap(int m,int n){
char[][] map=new char[m][n];
for(int i=0;i<m;i++) map[i]=next().toCharArray();
return map;
}
}
}