第二回 アルゴリズム実技検定 J – ⽂字列解析 解説

問題

問題文は本家サイトにあります:第二回 アルゴリズム実技検定 J – ⽂字列解析

問題概要

英小文字と ‘(‘ と ‘)’ からなる文字列 $S$ が与えられます。以下の操作をこれ以上操作が行えなくなるまで繰り返したとき、最終的な文字列 $S$ を答えてください。

 操作:'(‘ や ‘)’ を含まない文字列 $T$ に対し、'(‘ + $T$ + ‘)’ のような連続部分文字列が $S$ にあったとき、その部分文字列を $T+rev(T)$ に置き換える。
 ここで、 $T$ は空文字でもよい。 $rev(T)$ は、文字列 $T$ を反転させた文字列を表す。

制約

$S$ の長さは1以上1000以下。
最終的な文字列は、1000文字を超えず、'(‘ や ‘)’ を含まないように、 $S$ が与えられる。

解説

リンク先の入力例3で考えてみます。

(d(abc)e)()

この中で、まず、カッコを含まず、まわりがカッコで囲まれている文字列を探すと、abc が見つかります。これを操作により置き換えると

(dabccbae)()

となります。再び探すと、dabccbae が見つかるので、再び置き換えると

dabccbaeeabccbad()

となります。最後のカッコは、空文字がカッコで囲まれていると考えます。() は操作によって空文字に変わるので

dabccbaeeabccbad

となり、これが最終的な文字列になります。

この操作をどのようにやっていくかですが、順番に左から操作をしていけばいいでしょう。英小文字や ‘(‘ の場合は何もできないですが、 ‘)’ が現れれば操作ができます。

‘)’ がはじめてあらわれたとき、そこからさかのぼって、'(‘ を見つければいいですね。このように、データを貯めておいて、後に追加したものから順番にさかのぼっていくには、スタック stack を使うと便利です。

【広告】

対応するカッコが見つかったとして、今度はカッコの内側にある文字列と、その反転した文字列とをくっつける作業を考えないといけません。ただ、これも、スタックを利用すればいいですね。

先ほどの例であれば、スタックには (d(abc と入れた後、 ‘)’ が見つかるので、スタックから値を取り出す処理を始めていきます。1つ取り出すと c が出てくるので、これを2つくっつけて cc とします。もう1つ取り出すと b が出てくるので、前と後ろにくっつけて bccb とします。もう1つ取り出すと a が出てくるので、前と後ろにくっつけて
abccba とします。次に取り出したものは ‘(‘ なので、ここれ置き換える操作はおしまいです。これを再びスタックに入れて、操作を繰り返していきます。

カッコの対応は正しくなるように与えられるので、この操作を繰り返していけば、最終的な文字列が得られます。以上のことを踏まえると、C++で書けば、次のようになります。コードの下に、コードの説明が続きます。

#include <iostream>
#include <stack>
using namespace std;

int main() {

  string S; cin >> S;
  stack<char> st;
  for (int i = 0; i < S.size(); i++) {
    if (S[i] != ')') {
      st.push(S[i]);
    } else {
      string temp = "";
      while (true) {
        char c = st.top(); st.pop();
        if (c == '(') break;
        temp = c + temp + c;
      }
      for (int j = 0; j < temp.size(); j++) {
        st.push(temp[j]);
      }
    }
  }
 
  string ans = "";
  while (!st.empty()) {
    char c = st.top(); st.pop();
    ans = c + ans;
  }
  cout << ans;
  return 0;
}

文字列 $S$ を左から見ていきます。 ‘)’ 以外の場合は、スタックに追加していきます。 ‘)’ の場合は、文字列を順番に取り出し、 temp をはさんでいく、という処理を、'(‘ が出てくるまで繰り返します。これで、部分文字列とその反転をつなげたものが得られます。

$S$ の最後の文字まで処理すれば、あとはスタックに入っている結果を表示しておしまいです。