🏠 Home / 競プロ / PAST

第二回 アルゴリズム実技検定 D - パターンマッチ 解説

問題

問題文は本家サイトにあります:第二回 アルゴリズム実技検定 D - パターンマッチ

問題概要

英小文字から成る文字列 $S$ が与えられます。文字列 $T$ が次の条件を満たすとき、 $T$ は $S$ とマッチする、ということにします。

 条件: $T$ は英小文字と '.' からなる文字列で、'.' をそれぞれ好きな英小文字に置き換えると、 $S$ のある連続部分文字列と一致するようにできる。

長さが $1$ 以上 $3$ 以下の文字列で $S$ とマッチするものの個数を答えなさい。

制約

$S$ は英小文字から成る文字列で、長さは $1$ 以上 $100$ 以下。

解説

リンク先の入力例にもありますが、ab なら、'a', 'b', 'ab' 以外に、'.', 'a.', '.b', '..' があり、7個となります。このくらいなら書き出せますが、他の入力例 'aabbaabb' を考えると大変そうですね。

'aabbaabb' の例を考えてみましょう。3文字でこれとマッチするものを考えてみます。'.' を含まないものは、前から順番に考えると、 'aab', 'abb', 'bba', 'baa' の4つだとわかります。他はダブってしまいます。また、 '.' を含むものは、これらの文字列のどれかを '.' に変えたものだけになることがわかるでしょう。例えば、 'aab' をベースにすれば、 'a..' や '.ab' などが該当します。

この例からもわかりますが、'.' を含まない文字列を考えた場合でも、結構重複があります。 '.' を含むものも重複が発生するので、重複を除いて正確に数えるのはすごく難しそうです。

こうした場合には、集合 set を使うといいです。set は、重複するデータをもつことができませんが、この性質を使って、とりあえず条件に合うものをどんどんいれていけばいいです。

$S$ から、1文字、2文字、3文字の連続部分文字列を順番に抜き出します。これらに対して、いくつかの場所を '.' に変えたものを作ります(「いくつか」は0でもよい、つまり、'.' に変えなくてもOK)。こうしてできる文字列を set に追加していき、要素数を返せば答えになります。

'.' に置き換えた文字列を作るところは、ビット演算を使うとやりやすいでしょう。例えば、'aab' という3文字の場合に、'.' で置き換えた文字列を作る方法を考えます。これは、2進数の $000_{(2)}$ から $111_{(2)}$ を用意して、 $1$ となっている場所を '.' に置き換える、というような処理でできます。

以上のことを踏まえれば、C++では、以下のようにして書くことができます。(下のコードでは、先ほどの説明とは異なり、 $1$ の場所と '.' の箇所が左右反転していますが、全列挙しているので集合としては同じ結果になります。)

#include <iostream>
#include <set>
#include <string>
using namespace std;

set<string> st;

void update(string s) {
  int N = (1 << s.size());
  for (int i = 0; i < N; i++) {
    string t = s;
    for (int j = 0; j < s.size(); j++) {
      if (i & (1 << j)) t[j] = '.';
    }
    st.insert(t);
  }
}

int main() {
  string S; cin >> S;

  for (int i = 0; i < S.size(); i++) {
    update(S.substr(i, 1));
    update(S.substr(i, 2));
    update(S.substr(i, 3));
  }
  
  cout << st.size();
  return 0;
}

関連するページ

YouTubeもやってます

チャンネル登録はコチラから (以下は、動画のサンプルです)
慶應義塾大学薬学部2024年度数学第1問5 同志社大学文系2024年度数学第1問3 昭和大学医学部I期2024年度数学第2問 兵庫医科大学2024年度数学第3問 共通テスト2B2024年度第3問2のヒントについて 久留米大学医学部推薦2024年度数学第4問