F - substr = S 解説 by yfuka86


桁DPの要領でまとめて計算することが可能です。
本質的な計算対象や、R以下とL-1以下の場合をそれぞれ計算し差を取るパートは公式解説と同じです。

部分問題において求める\(x\)以下に関する答えを\(f(x)\)とし、これを考えます。

\( dp[i][j][k][l] \) := 上から\(i\) 桁目まで決定している場合の数で、

  • \(S\)\(j\) 文字目まで一致している
  • \(i\)桁目まで最大値\((=x)\)と一致している・いない (\(k = {1, 0}\) )
  • \(i\)桁目までで0以外の数値が1回以上現れている・いない (\(l = {1, 0}\) )

と定め、\(dp[0][0][1][0] = 1\)と初期化します。

遷移を以下のように定めます。\(d\)を次に決める桁の数とし、\([0, 9]\)で(\(x\)を超えない範囲で)ループを回します。
\(nk := ( k=1かつ dとxのi桁目が一致している: 1, それ以外: 0 )\)
\(nl := (l=1またはdが0でない: 1, それ以外: 0)\)
とすると、

  • \(j=0\) のとき
    \(dp[i + 1][j][nk][nl]\) += \(dp[i][j][k][l] \)

  • \(j\in[0,|S|)\) のとき
    \(d\)\(S\)\(j+1\)文字目が一致し、\(nl\)\(1\)である場合のみ
    \(dp[i + 1][j + 1][nk][nl]\) += \(dp[i][j][k][l] \)

  • \(j=|S|\)のとき
    \(dp[i + 1][j][nk][l]\) += \(dp[i][j][k][l] \)
    \(l=0の場合は0が入っている\)

と場合分けして遷移します。順にSとの一致前、Sとの一致、Sと一致後の数え上げに相当します。
結果、\(f(x) = dp[xの桁数][|S|][0][1] + dp[xの桁数][|S|][1][1] \)となります。

実装例(C++):https://atcoder.jp/contests/abc295/submissions/40103568

P.S. 筆者はkについてxと一致している場合1としていますが、これと逆にxより真に小さいことが確定している場合1とするのがより一般的なようです。

投稿日時:
最終更新: