動的計画法で文字列から別の文字列を拾い出す方法を数える

文字列 s から、順番を変えずに文字を拾って文字列 t を作る拾い方が何通りあるかを数えます。t の前から何文字までを作ったかごとの数を、s の文字を 1 つずつ読みながら更新する練習です。

海外で定番の問題リスト 4 本には入っていません

問題

文字列 s と t を受け取ります。s から文字をいくつか選び、元の順番のままつなげると t になる選び方が何通りあるかを返す関数を書いてください。

  • 選ぶ文字は隣どうしでなくてかまいません。s の中の場所の組が違えば、別の選び方として数えます
  • t が s から作れなければ 0 を返します
  • s と t の長さは 1 以上 1000 以下で、英小文字だけです
  • 答えは 2^31 未満になるものとします。t の前から何文字かだけを作る数え方も、同じく 2^31 未満に収まるものとします

入力と出力の例

例 1

b は 0 番の 1 つです。a を 1 番から選ぶと n は 2 番か 4 番の 2 通り、a を 3 番から選ぶと n は 4 番の 1 通り、a を 5 番から選ぶと後ろに n がありません。合わせて 3 通りです。

s = "banana" t = "ban"
3

例 2

m は 2 番か 3 番、i は 4 番、t は 5 番か 6 番から選べるので、2 × 1 × 2 で 4 通りです。

s = "committee" t = "mit"
4

例 3

s の中で a は c より後ろにあるので、a、c、t の順には拾えません。

s = "cat" t = "act"
0

面接での聞かれ方

面接では、2 つの文字列を表の縦と横に取る動的計画の中でも、数え上げの難しい 1 問として出されます。

  • 「s の部分列で t と等しくなる物は、いくつありますか」
  • 最初に「同じ中身でも、拾う場所が違えば別に数えますか?」と確かめられるかを見られます
  • 書き終えると「表を 1 行分に減らすとき、どちら向きに埋めますか?」と聞かれます

最長共通部分列の表と似ていますが、「最大」ではなく「足し算」で埋める点が違います。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「動的計画法 (DP)」に、この型の要点を表とコードでまとめています。

動的計画法は、途中の答えを覚えておいて、同じ計算をやり直さない書き方です。漸化式で言い直せる問題が多いです。

main.py
def count_picks(s: str, t: str) -> int:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(count_picks("banana", "ban"))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します