第二回 アルゴリズム実技検定 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 は、重複するデータをもつことができませんが、この性質を使って、とりあえず条件に合うものをどんどんいれていけばいいです。

【広告】
グラフ理論、アルゴリズム、最適化、計算理論、線形計画法…ほぼすべての定理に簡潔な証明を記述。検索しやすい記法一覧、問題一覧、アルゴリズム一覧、充実の索引3000項目。原著最新版に対応した完全アップデート版。
著者:平田 富夫
出版社:丸善出版
発売日:2012-02-29
ページ数:717 ページ
値段:¥13,200
(2020年09月 時点の情報です)

$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;
}