Codeforces Round #556 B. Three Religions
お題箱より。
問題概要
英小文字からなる長さ の文字列 が与えられる。また文字列 があり、これらは最初は空である。
以下のクエリが合計で 個与えられる。
1 i c
文字列 の末尾に文字 を足す。2 i
文字列 の末尾の文字を取り去る。
各クエリに対して、そのクエリを処理した後の に対する以下の問いに答えよ。
- の文字を色1、色2、色3、無色に塗り分け、色 に塗られた文字を順に読むと に一致するようにできるか?
制約
- 文字列 および1クエリで与えられる文字は全て英小文字である
- 空文字列に対して2クエリが与えられることはない
- 全ての時点で はいずれも250文字を超えない
解法
問題1回の解き方を考える
まずはクエリのことを忘れて、1回問題を解くことを考えましょう。
文字列を左から見ていくことで、例えばこんなDPが考えられます。
- 文字列 の最初の 文字目まで見たときに、 のそれぞれ 文字目までが塗られているようにできるか?(true/false)
これでは状態数が多いですね。ここで「できる/できない」などのブール値を値とするDPは、その添字のうちの1つを値にしてしまうことで状態数を削減できる可能性があります。例えば、
- のそれぞれ 文字目までが塗られているようにするためには、最小で文字列 の最初の 文字目までを見ればよいか?(ただし、不可能な場合はINFとする)
- 文字列 の最初の 文字目まで見て、 はそれぞれ 文字目まで塗られているときに、 は最大で何文字まで塗られているようにできるか?
のような変形が考えられます。このように添字の1つを「最小の○○」「最大の○○」のように値のほうに持ってくることで状態数を削減できます。今回の場合は の値がそれぞれ250以下であることから、上のほうを採用したいです。
あとはちゃんと遷移を計算できるようにします。例えば のとき、 は何になるでしょうか?それは「 の 文字目と同じ文字が、 の 文字目より後ろで一番近くにあるところ」となります。
これは事前に「 の 文字目より後ろで、文字 が一番近くにあるのはどこか?」という情報を全ての に対して計算しておけばすぐに求められます。これは後ろから見ていくことで全て求められて、文字は26種類なので十分間に合います。
の長さをそれぞれ として、 がINFでなければ答えはYesです。ということで、判定を1回するだけなら解けそうです。
クエリへの対応を考える
クエリのたびにさっきのDPをやり直していては間に合いません。クエリでは どれかの末尾が1文字増減するだけなので、ほとんどの結果は使い回せるはずです。
クエリが来る前の の長さをそれぞれ としましょう。そして、この時点で の値が に対して既に求まっているとしましょう。ここにクエリが来た時の処理を考えます。
クエリ2の場合
文字を削除するクエリの場合は簡単です。例えば 2 1
というクエリが来ると の長さは になります。このとき の結果はそのまま使うことができます。この値がINFでなければYesです。
クエリ1の場合
文字を追加するクエリの場合は、新しく計算をするべき部分が出てきます。ただ使い回せる部分は使いましょう。
例えば 1 1 <何か>
というクエリが来ると の長さは になります。このときは を新しく計算する必要があります。
この更新部分は が最大でそれぞれ0~250まで動き得るので 箇所で、1つに対する遷移は高々3通りです。新しく更新する場所についてループを回したほうが分かりやすいので、実装は貰うDPがオススメです。
これを計算した後に がINFでなければYesです。クエリ数が1000以下なので、ちょっと厳しいですが何とか間に合います。