Problem:
JJOOII (JJOOII)

JOI (日本情報オリンピック) の本選に向けてプログラミングの練習をしていたあなたは，今年度のJOI の予選の問題には数値を扱う問題ばかりが出題され，文字列を扱う問題がなかったことに気がついた．そこであなたは，こっそり文字列の問題に強くなってライバルたちに差をつけることにした．

JOI の過去問を眺めていると，J, O, I の3 種類の文字からなる文字列に慣れておく必要がありそうなことがわかった．そこで，そのような文字列について考えよう．あなたは「与えられた文字列がJOI という部分文字列をもつかどうかを答えよ」という問題を思いついたものの，これはすぐに解けてしまった．もっとレベルの高い問題を解きたいあなたは，以下のような問題を作った．

文字列t が文字列s の部分文字列であるとは，t の先頭および末尾に何文字か(0 文字でもよい) を付け足すとs になることである．たとえば，JJOOII はOJJOOIIOJOI の部分文字列である．一方，JOI はJOOI の部分文字列ではない．

また，0 以上の整数k に対し，レベルk のJOI 列とは，k 個の文字J，k 個の文字O，k 個の文字I をこの順に並べた文字列のことであるとする．たとえば，JJOOII はレベル2 のJOI 列である．与えられた文字列の部分文字列であるJOI 列のうち，レベルが最大のものを求めたい．

課題

J, O, I の3 種類の文字からなる長さN の文字列S が与えられたとき，レベルk のJOI 列がS の部分文字列であるような最大のk の値を求めるプログラムを作成せよ．

制限

1 ≤ N ≤ 1000000 (= 106)   S の長さ

入力

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

1 行目にはJ, O, I の3 種類の文字からなる文字列S が書かれている．

出力

標準出力に，レベルk のJOI 列がS の部分文字列であるような最大のk の値を表す整数を1 行で出力せよ．

採点基準

採点用データのうち，配点の20%分については，N ≤ 100 を満たす．

入出力例

入力例 1

OJJOOIIOJOI

出力例 1

2

OJJOOIIOJOI はレベル2 のJOI 列であるJJOOII を部分文字列として含んでおり，レベル3 以上のJOI列は部分文字列として含まない．

入力例 2

IJJIIJJJ

出力例 2

0

入力例 3

JOIJOIJOIJOIJOI

出力例 3

1

入力例 4

OOJJJJJJJOOOOIIIII

出力例 4

4

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

Solution in C++ (judge verdict: Time Limit Exceeded)

#include "stdio.h"

int num;
char letter[1000001] = {};
int sum[3][1000001] = {};
int ans = 0;
bool flag = 1;

int main() {
	//FILE *file;
	//file = fopen("1.txt", "r");
	scanf("%s", letter);
	for (int i = 0; i < 1000001; i++) {
		for (int j = 0; j < 3; j++) {
			sum[j][i + 1] = sum[j][i];
		}
		if (letter[i] == 'J')sum[0][i + 1]++;
		else if (letter[i] == 'O')sum[1][i + 1]++;
		else if (letter[i] == 'I')sum[2][i + 1]++;
		else {
			num = i;
			break;
		}
	}
	for (int i = 0; i < num; i++) {
		if (letter[i] == 'J') {
			for (int j = i + 3+ans*3; j <= num; j += 3) {		
			//	printf(" %d\n", j);
				if ((sum[1][j] - sum[1][i]) > (sum[0][j] - sum[0][i]))break;
				if ((sum[2][j] - sum[2][i]) > (sum[0][j] - sum[0][i]))break;
				if ((sum[2][j] - sum[2][i]) > (sum[1][j] - sum[1][i]))break;
				if (sum[1][j] - sum[1][i] != 0 || sum[2][j] - sum[2][i] != 0) {
					if (sum[0][j] - sum[0][i] != sum[0][i + (sum[0][j] - sum[0][i]) * 3] - sum[0][i])break;
					if (sum[0][i + (sum[0][i + (sum[0][j] - sum[0][i]) * 3] - sum[0][i]) * 3] - sum[0][i] != sum[1][i + (sum[1][i + (sum[0][j] - sum[0][i]) * 3] - sum[1][i]) * 3] - sum[1][i])break;
					if (sum[2][i + (sum[2][i + (sum[0][j] - sum[0][i]) * 3] - sum[2][i]) * 3] - sum[2][i] != sum[1][i + (sum[1][i + (sum[0][j] - sum[0][i]) * 3] - sum[1][i]) * 3] - sum[1][i])break;
					if (sum[0][i + (sum[0][i + (sum[0][j] - sum[0][i]) * 3] - sum[0][i]) * 3] - sum[0][i] != sum[2][i + (sum[2][i + (sum[0][j] - sum[0][i]) * 3] - sum[2][i]) * 3] - sum[2][i])break;
					flag=true;
					for (int k = 0; k < sum[0][j] - sum[0][i]; k++) {
						if (letter[i + k] != 'J') {
							flag = false;
							break;
						}
					}
					if (!flag) break;
					for (int k = 0; k < sum[0][j] - sum[0][i]; k++) {
						if (letter[i + k + sum[0][j] - sum[0][i]] != 'O') {
							flag = false;
							break;
						}
					}
					if (!flag) break;
					for (int k = 0; k < sum[0][j] - sum[0][i]; k++) {
						if (letter[i + k + (sum[0][j] - sum[0][i])*2] != 'I') {
							flag = false;
							break;
						}
					}
					if (ans < sum[0][j] - sum[0][i])ans = sum[0][j] - sum[0][i];
					i += (sum[0][j] - sum[0][i]) * 3 - 1;
					break;
				}
			}
		}
	}
	printf("%d\n", ans);
	return 0;
}