動的計画法でドットとアスタリスクの入った型が文字列全体に合うか調べる

「どの 1 文字でもよい」のドットと「直前の 1 文字を 0 回以上くり返す」のアスタリスクを使った型が、文字列の全体に合うかを確かめます。文字列と型の前から何文字ずつが合うかを表にして埋める練習です。

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

問題

文字列 s と型 p を受け取ります。p が s の全体に合うかを返す関数を書いてください。型の文字は次の決まりで読みます。

  • 英小文字は、同じ文字 1 つに合います
  • . (ドット) は、どの英小文字 1 つにも合います
  • * (アスタリスク) は、すぐ前の 1 文字 (英小文字か .) を 0 回以上くり返します。a* は空、a、aa、aaa、… のどれにも合います
  • 合うのは s の全体だけです。s の一部だけに合っても false です
  • s の長さは 0 以上 20 以下で英小文字だけ、p の長さは 1 以上 20 以下で英小文字と . と * だけです
  • * は p の先頭には来ず、* が 2 つ続くこともありません

入力と出力の例

例 1

b が b に、a* が aaa に、d が d に合います。

s = "baaad" p = "ba*d"
true

例 2

a* は a をくり返すだけなので、b には合いません。

s = "ab" p = "a*"
false

例 3

z* も y* も 0 回くり返す (空) と読めば、空の文字列に合います。

s = "" p = "z*y*"
true

面接での聞かれ方

面接では、文字列と型を表の縦と横に取る動的計画の、いちばん難しい部類の 1 問として出されます。

  • 「. と * だけの簡単な正規表現が、文字列全体に合うかを判定してください」
  • 最初に「* は直前の 1 文字だけにかかりますか?」「一部が合えばよいですか、全体ですか?」と確かめられるかを見られます
  • 書き終えると「.* はどう動きますか?」「空の文字列と a*b* は?」と端の場合を聞かれます

* が「直前の文字ごと消える」場合と「もう 1 文字食べる」場合の 2 通りを、表の言葉で言えるかが見どころです。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

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

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

main.py
def pattern_match(s: str, p: str) -> bool:
    """
    Goal: 何を返すか

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

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

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


# 「実行」を押すと、この行の結果が出ます
print(pattern_match("baaad", "ba*d"))

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