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 Haskell (judge verdict: Accepted)

import Control.Applicative
import Control.Monad
import Control.Monad.ST
import Data.Functor
import Data.Function
import Data.Monoid
import Data.Maybe
import Data.List
import qualified Data.Foldable as Foldable
import qualified Data.Set as Set
import qualified Data.Sequence as Sequence
import Data.List.Split
import Data.Bits
import Data.Char
import Data.Ix
import Data.Ratio
import Data.Ord
import Data.Tuple
import Data.Array
--import Data.Array.Unboxed
import Data.Array.MArray
import Data.Array.IO
import Data.Array.ST
import Data.IORef
import Data.STRef
-- import System.IO.Unsafe
 
readInt = read :: String -> Int
readInteger = read :: String -> Integer
readDouble = read :: String -> Double
getInt = readLn :: IO Int
getInts = map readInt . words <$> getLine
getInteger = readLn :: IO Integer
getIntegers = map readInteger . words <$> getLine
getDouble = readLn :: IO Double
sjoin :: (Show a) => [a] -> String
sjoin = unwords . map show
cond :: a -> a -> Bool -> a
cond t f c = if c then t else f
apply2 :: (a -> a -> b) -> [a] -> b
apply2 f [x,y] = f x y
apply3 :: (a -> a -> a -> b) -> [a] -> b
apply3 f [x,y,z] = f x y z
apply4 :: (a -> a -> a -> a -> b) -> [a] -> b
apply4 f [x,y,z,w] = f x y z w
fnBin :: (b -> c -> d) -> (a -> b) -> (a -> c) -> a -> d
fnBin op f g x = op (f x) $ g x
fnTuple :: (a -> b, a -> c) -> a -> (b, c)
fnTuple (f,g) a = (f a, g a)
replace :: (Eq a) => a -> a -> [a] -> [a]
replace x y = map (\z -> if z==x then y else z)
modifyArray :: (Ix i, MArray a e m) => a i e -> i -> (e -> e) -> m ()
modifyArray arr ix f = readArray arr ix >>= writeArray arr ix . f
-- end of templete

f n [] = n
f n (('J',x):('O',y):('I',z):s)
  | x>=y && y<=z = f (max n y) s
  | otherwise = f n s
f n (t:s) = f n s

main = getLine >>= print . f 0 . map (fnTuple (head,length)) . group