G - 辞書順 Editorial by Kiri8128


\(N = |s|\) とし、アルファベットの数を \(M\) とします。 \(i\) 文字目以降に初めてアルファベット \(x\) が出てくる位置を \({\rm next}_{i, x}\) とします。 これは後ろからDPすれば求まります。

「異なる位置であっても文字列として同じであれば区別しない」という条件は、「文字列として同じものは、選ぶ位置が辞書順で最小となるもののみ選ぶ(※)」と言い換えられます。

\(i\) について、 \(i\) 文字目以降で作れる文字列の個数 \(X[i]\) が分かっていれば、答えを前から順番に試すことで \(O(NM)\) で求められます。 例えば、 \(x\) で始まる部分列は \(X[{\rm next}_{i, x}]\) なので、 \(x\) を “a”、 “b” と順番に見て合計がいつ \(K\) を超えるかを調べることで答えの \(1\) 文字目が分かります。 \(2\) 文字目以降も同様です。

あとは \(X[i]\) を求められれば良いですが、これも後ろからのDPで求められます。(※)に注意すると次の式が成立します。

\(X[i] = X[i+1] * 2 - X[{\rm next}_{i}]\)

なおこのままやると \(X[i]\) が大きくなり過ぎてしまいますが、本問では \(K\) 以下しか使わないので適当に floor して良いです。 以上により本問は \(O(NM)\) で解くことができました。

参考:AC コード(PyPy 3)

この提出では \({\rm next}_{i, x}\) を愚直に \(N\times M\) 配列で持つと MLE したので、 \({\rm next}_{i}={\rm next}_{i, s(i)}\) のみ配列で持って、後は逐次更新しています。

posted:
last update: