お題箱より。
問題概要
カッコ列 が与えられる。「正しい」カッコ列であって、
の両方を部分列として含むようなカッコ列のうち、長さが最小のものを1つ求めよ。
制約
解法
文字列のインデックスは0-indexedで表記します。
まずは正しいカッコ列にこだわらず、「文字列 の両方を部分列として含むような長さ最小の文字列」を求めることを考えます。これは最長共通部分列(LCS)として知られるものとよく似たDPで解くことができます。
:文字列
の
文字目までと文字列
の
文字目までの両方を部分列として含む、長さ最小の文字列の長さ
このDPの からの遷移は以下の3通りです。
の
文字目を末尾に付ける。長さを
足して
に遷移する。
の
文字目を末尾に付ける。長さを
足して
に遷移する。
の
文字目と
の
文字目が等しい場合、その文字を末尾に付ける。長さを
足して
に遷移する。
カッコ列についても、これと似たようなことをします。こちらは「正しい」カッコ列でなければならないという条件があるので、これまでの を持っておくことにします。これを「深さ」と呼ぶことにします。
:カッコ列
の
文字目までとカッコ列
の
文字目までの両方を部分列として含み、深さが
であるもののうち、長さ最小のカッコ列の長さ
遷移は末尾に開きカッコを付ける場合と閉じカッコを付ける場合の2通りを考え、それぞれ文字を付けた時に の添字が進むかどうかをチェックすれば良いです。
の範囲には遷移しません。
このDPの添字として扱うべき範囲を考えましょう。 の最大値は
、
の最大値は
なので
以下です。
として扱うべき値の範囲を考えます。
ここで ()
が 個続くような文字列
()()()......()
は必ず条件を満たすことに注目します。つまり答えは必ず 以下です。もしDPの遷移の途中で
が
を超えた場合、その開きカッコを全て閉じる必要があるので文字列の長さは必ず
を超えます。そのため、
としても
以下の範囲だけを考えれば良いです。
状態数が で各遷移が
通りなので、十分間に合います。答えとなる文字列の長さは
として求められますが、実際の文字列を求める必要があるので、地道に各添字の組について遷移元の添字の組を持っておいて復元しましょう。具体的にはACコード例を参考にしてください。
ACコード
Submission #67019853 - Codeforces
DPの更新順序について、 が減ることはないですが、
は増えたり減ったりするので注意です。
は普通の2重ループで小さい方から順番に見ていって、
- 遷移で
を増やす(つまり
(
を付ける)場合は、が小さい方から見ていく
- 遷移で
を減らす(つまり
)
を付ける)場合は、が大きい方から見ていく
ようにすれば良いです。このようにしないと、例えば が一切変化しないまま
))))))...
を付けて を大量に減らす、というような操作を見逃す可能性があります。