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)を利用したアプローチが非常に簡潔です。
- パターンの構築:
同じ文字の連続を見つけるために、
([a-z])\1*という正規表現を使用します。([a-z]): 任意の英小文字1文字をキャプチャし、グループ1とします。\1*: グループ1でマッチした文字と同じ文字が0回以上繰り返される部分にマッチします。
- マッチングの実行:
re.finditerを使うことで、文字列全体からこのパターンに一致する部分(ラン)を順番に取り出すことができます。 - 変換処理:
各マッチに対して、以下のルールで文字列に変換します。
- 連続する文字の種類を
m.group(1)で取得。 - 全体の長さ(連続数)を
len(m.group(0))で取得。 - 長さが1より大きければ「文字 + 長さ」、1であれば「文字」のみ。
- 連続する文字の種類を
- 結合と出力: 得られた各パーツを連結して出力します。
計算量
- 時間計算量: \(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 によって生成されました。
投稿日時:
最終更新: