ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

Typical DP Contest G - 辞書順

問題
提出コード

解法

まず、

  • m_{i,j} = i文字目の次にjという文字が現れるインデックスの最小値

を計算します。
これは、m_{i-1,j} = m_{i,j}(j \neq s_{i}), m_{i-1,s_{i}} = iという更新を行えばよいです。
さて、

  • dp[i] = i文字目を先頭で使った際にできる、部分文字列の種類数

というDPを考えます。
i文字目を使った際に考えられるパターンは、

  • i文字目の1文字のみ

  • i文字目の次に、jという文字を繋げる

の2つです(後者は最大26通りありますが…)。前者は明らかにトータル1通りです。
後者については、iの直後に現れるjを取ってくると、i番目の文字の次にjを置いたパターンの部分文字列が網羅できます。
なので、結局
dp[i] = 1 + \sum_{j} dp[m_{i,j}]
という遷移になります。
では、最終的に求めたい文字列はどうなるでしょうか。
まず、先頭の文字がa,b,...zであるような部分文字列全てについて足してもKにならない場合は、明らかにEelとなります。
それ以外の場合は、前から貪欲、aから順番に見ていき、総和がK以上になるならその文字を選択…という操作を繰り返します。
実装の際は、先頭にダミーの文字を入れると実装が少し楽になります。

今見ているインデックスをidとして、次に選ぶ文字をaから順番に見ていきます。dp[m_{id,j}]を見て、これがK以上ならばjが次の文字になります。そうでなければ、Kからdp[m_{id,j}]を引き、次の文字をチェックしていきます。
今見ている文字が最後の場合となるパターンをKから引き忘れないようにすれば、答えが求まります。

感想

最近ABCやその他の場所で、ある状態を構成し数え上げる際に重複するパターンを、貪欲にチェックする操作のみ考慮することで重複を消す、という考え方を使ってきたので、それと似たような考え方でできました。
それよりむしろ、メモリ制限が厳しかったです…空間計算量はまだまだ難しいですね