公式

B - データ圧縮 / Data Compression 解説 by admin

Gemini 3.0 Flash

概要

与えられた文字列 \(S\) に対して、同じ文字が連続している部分を「文字 + 連続回数」という形式に置き換える「ランレングス圧縮(連長圧縮)」を行う問題です。ただし、連続回数が1回の場合は数字を省略するというルールがあります。

考察

この問題を解くためのポイントは、「どこで文字が切り替わるか」を正確に把握することです。

例えば、aaabbc という文字列を考えます。 1. a が 3 つ続く(aaa)→ a3 2. 次に b が 2 つ続く(bb)→ b2 3. 次に c が 1 つだけ現れる(c)→ c(1は省略)

文字列の長さ \(|S|\) は最大で \(2 \times 10^5\) と比較的大きいため、文字列を先頭から1文字ずつ走査し、効率的に処理する必要があります。

効率的な文字列操作

Pythonなどの言語では、文字列の結合(s = s + "a")を繰り返すと、その度に新しい文字列オブジェクトが生成されるため、計算時間が \(O(|S|^2)\) になり、実行時間制限(TLE)に間に合わない可能性があります。そのため、リストに結果を格納してから最後に一括で結合する("".join(list))手法や、正規表現、あるいはイテレータを活用するのが効率的です。

アルゴリズム

正規表現(Regular Expression)を利用したアプローチが非常に簡潔です。

  1. パターンの構築: 同じ文字の連続を見つけるために、([a-z])\1* という正規表現を使用します。
    • ([a-z]) : 任意の英小文字1文字をキャプチャし、グループ1とします。
    • \1* : グループ1でマッチした文字と同じ文字が0回以上繰り返される部分にマッチします。
  2. マッチングの実行: re.finditer を使うことで、文字列全体からこのパターンに一致する部分(ラン)を順番に取り出すことができます。
  3. 変換処理: 各マッチに対して、以下のルールで文字列に変換します。
    • 連続する文字の種類を m.group(1) で取得。
    • 全体の長さ(連続数)を len(m.group(0)) で取得。
    • 長さが1より大きければ「文字 + 長さ」、1であれば「文字」のみ。
  4. 結合と出力: 得られた各パーツを連結して出力します。

計算量

  • 時間計算量: \(O(|S|)\) 文字列を一度スキャンしてマッチングを行うため、文字列の長さに比例した時間で処理が完了します。
  • 空間計算量: \(O(|S|)\) 圧縮後の文字列を保持するためのメモリが必要になります。

実装のポイント

  • 正規表現の活用: re.finditer を使うことで、ループ処理を自前で書くよりも簡潔かつ高速に「同じ文字の塊」を抽出できます。

  • 個数の判定: if len > 1 という条件分岐を三項演算子などで記述することで、個数が1の場合の例外処理(数字を書かない)をスマートに実装できます。

  • 高速な入出力: sys.stdin.read().strip() や sys.stdout.write を使用することで、大量のデータに対しても高速に動作するように工夫しています。

    ソースコード

import re
import sys

def solve():
    # 標準入力から文字列Sを読み込みます
    # sys.stdin.read().strip() を使用することで、末尾の改行や空白を除去します
    s = sys.stdin.read().strip()
    
    if not s:
        return

    # 正規表現を使用して、同じ文字が連続する部分(ラン)を抽出します。
    # ([a-z]) : 英小文字1文字をキャプチャし、グループ1とします。
    # \1*     : グループ1と同じ文字が0回以上繰り返される部分にマッチします。
    # re.finditer はマッチした各部分をイテレータとして返します。
    matches = re.finditer(r'([a-z])\1*', s)

    # 各マッチオブジェクト m に対して以下の処理を行います:
    # m.group(1) : 連続する文字の種類の1文字目
    # m.group(0) : マッチした文字列全体(例: "aaa")
    # len(m.group(0)) : その文字が連続している個数
    # 個数が1より大きい場合は「文字 + 個数」、1の場合は「文字」のみをリストに追加します。
    result = (m.group(1) + (str(len(m.group(0))) if len(m.group(0)) > 1 else "")
              for m in matches)

    # 最後にすべての結果を連結して出力します
    sys.stdout.write("".join(result) + "\n")

if __name__ == '__main__':
    solve()

この解説は gemini-3-flash-preview によって生成されました。

投稿日時:
最終更新: