[競プロ用]ランレングス圧縮まとめ

# いつ使うのか

* 与えられる文字列で連続する回数・個数に着目する問題。

# 何がいいのか

画像圧縮などに利用される手法。膨大なデータを可逆性を損なわずに圧縮できる。
ただし、元データよりも必ずしもデータ量が少なくなるとは限らない。連続する構造の少ないデータでは逆に増加することもある。

# どうするのか

同じ文字が何回繰り返されるかの文字と数字の組み合わせで表す。

# テンプレート

```python
from itertools import groupby

# RUN LENGTH ENCODING str -> list(tuple())
# example) "aabbbbaaca" -> [('a', 2), ('b', 4), ('a', 2), ('c', 1), ('a', 1)] 
def runLengthEncode(S: str) -> "List[tuple(str, int)]":
    grouped = groupby(S)
    res = []
    for k, v in grouped:
        res.append((k, int(len(list(v)))))
    return res

# RUN LENGTH DECODING list(tuple()) -> str
# example) [('a', 2), ('b', 4), ('a', 2), ('c', 1), ('a', 1)] -> "aabbbbaaca"
def runLengthDecode(L: "list[tuple]") -> str:
    res = ""
    for c, n in L:
        res += c * int(n)
    return res

# RUN LENGTH ENCODING str -> str
# example) "aabbbbaaca" -> "a2b4a2c1a1" 
def runLengthEncodeToString(S: str) -> str:
    grouped = groupby(S)
    res = ""
    for k, v in grouped:
        res += k + str(len(list(v)))
    return res
```

# 使用例

## [ABC019 B - 高橋くんと文字列圧縮](https://atcoder.jp/contests/abc019/tasks/abc019_2)

```python
from itertools import groupby

# RUN LENGTH ENCODING
def runLengthEncodeToString(S: str):
    grouped = groupby(S)
    res = ""
    for k, v in grouped:
        res += k + str(len(list(v)))
    return res

def main():
    S = input()
    print(runLengthEncodeToString(S))
```

## [ABC143 C Slimes](https://atcoder.jp/contests/abc143/tasks/abc143_c)

```python
from itertools import groupby

def runLengthEncode(S: str) -> "List[tuple(str, int)]":
    grouped = groupby(S)
    res = []
    for k, v in grouped:
        res.append((k, int(len(list(v)))))
    return res

def main():
    N = int(input())
    S = input()
    print(len(runLengthEncode(S)))
    return
```

## [ABC136 D - Gathering Children](https://atcoder.jp/contests/abc136/tasks/abc136_d)

最終的にはRとLの境界に集まる。移動回数の偶奇によって最後の移動がどちらかがかわるので偶奇だけ見ればいい。

```python
from itertools import groupby

# RUN LENGTH ENCODING
def rfe_tuple(S: str):
    grouped = groupby(S)
    res = []
    for k, v in grouped:
        res.append((k, str(len(list(v)))))
    return res

def main():
    S = input()
    N = len(S)
    
    now = 0
    compressed = rfe_tuple(S)
    ans = [0] * N
    for lr, i in compressed:
        i = int(i)
        if lr == "R":
            now += i
            ans[now - 1] += i - i // 2
            ans[now] += i // 2
        else:
            ans[now - 1] += i // 2
            ans[now] += i - i // 2
            now += i
    
    L = [str(i) for i in ans]
    print(" ".join(L))
    return
```

## [ABC140 D - Face Produces Unhappiness](https://atcoder.jp/contests/abc140/tasks/abc140_d)

連続している部分を反転し、両端と繋がるようにすることで最大化できる。
両端はできれば反転させたくないので0番目は飛ばして奇数インデックスのみ反転させていく。

```python
from itertools import groupby

# RUN LENGTH ENCODING
def runLengthEncode(S: str):
    grouped = groupby(S)
    res = []
    for k, v in grouped:
        res.append((k, str(len(list(v)))))
    return res

def runLengthDecode(L: "list[tuple]"):
    res = ""
    for c, n in L:
        res += c * int(n)
    return res

def main():
    N, K = map(int, input().split())
    S = input()
    compressed = runLengthEncode(S)

    for k in range(K):
        i = 2 * k + 1   # 奇数インデックスのみ反転させる。
        if i >= len(compressed):
            break
        compressed[i] = ('L', compressed[i][1]) if compressed[i][0] == "R" else ('R', compressed[i][1])

    reCompressed = runLengthEncode(runLengthDecode(compressed))
    ans = N - len(reCompressed)
    print(ans)
```

# 参考

itertoolsレファレンス
https://docs.python.org/ja/3/library/itertools.html#itertools.groupby