ランレングス圧縮と復元
ランレングス圧縮(ランレングス符号化)とは、データの圧縮方法の一つで、圧縮前に正確に復元することができる可逆圧縮の一種です。
同じ文字が連続して何文字出現するかに変換します。
例:
In: aaallgoooooo
Out: a3l2g1o6
圧縮(encode)する際は Run Length Encoding, 復元(decode)する際は Run Length Decoding と言います。
アルゴリズム
ランレングス符号化: Encoding
- 以下を符号化したい文字列 S が空になるまで繰り返す
- S の最初の文字 c について、c が連続で何回出現するかを数えて、違う文字が出現するまで取り出す
- 答えの文字列に、c と 出現回数を後ろから加える
※ 「abcdefghijk」 が符号化で「a1b1c1d1e1f1g1h1i1j1k1」になるように、符号化前よりも、符号化後の方が文字列が長くなる場合があるので注意が必要です。
ランレングス符号化の復元: Decodeing
- 以下を復元したい文字列 S’ が空になるまで繰り返す
- S’ の最初の文字 c について、c とc が連続で何回出現するかを取り出す
- 答えの文字列に、c とを出現回数だけ加える
計算量
- 符号化:\(O(N)\)
- 復元:\(O(N)\)
もとの文字列のサイズをとすると、どちらも\(O(N)\) で計算できます。
C++での実装例
同じ文字列が連続で何回出現するのかなどを数える際は、しゃくとり法のように実装するとやりやすいでしょう。
※以下の実装例では、もとの文字列に数字が出現しないことを前提にしています。仮に出現する場合は、decode が正しくできません。
/* encode: ランレングス圧縮を行う str内には数字が出現しないと仮定 */ string encode(const string& str) { int n = (int)str.size(); string ret = ""; // 答えを格納 for (int l = 0; l < n;) { int r = l + 1; for (; r < n && str[l] == str[r]; r++) {}; ret.push_back(str[l]); string num_str = to_string(r - l); // 出現回数 ret += num_str; l = r; } return ret; } /* decode: ランレングス圧縮の復元を行う 元の文字列には数字が出現しないと仮定 */ string decode(const string& str) { int n = (int)str.size(); string ret = ""; // 答えを格納 for (int l = 0; l < n;) { int r = l + 1; for (; r < n && '0' <= str[r] && str[r] <= '9'; r++) {}; int num = stoi(str.substr(l + 1, r - l)); // 出現回数 for (int i = 0; i < num; i++) { ret.push_back(str[l]); } l = r; } return ret; }
C++での実装例(pair型を用いる場合)
先程の実装例では数字が文字列中に出現する場合は正しく実行できません。encode する際に文字 c と出現回数を分けて、pair 型で vector などに保存すると正しく decode できます。
/* encode: ランレングス圧縮を行う */ vector<pair<char, int>> encode(const string& str) { int n = (int)str.size(); vector<pair<char, int>> ret; for (int l = 0; l < n;) { int r = l + 1; for (; r < n && str[l] == str[r]; r++) {}; ret.push_back({str[l], r - l}); l = r; } return ret; } /* decode: ランレングス圧縮の復元を行う */ string decode(const vector<pair<char, int>>& code) { string ret = ""; for (auto p : code) { for (int i = 0; i < p.second; i++) { ret.push_back(p.first); } } return ret; }
練習問題
- [AtCoder] ABC 019 B – 高橋くんと文字列圧縮:ランレングス圧縮そのものです
- AtCoder Beginner Contest 136 D – Gathering Children:考察が少し必要です。pair型を用いたランレングス圧縮のライブラリがあると少し楽に実装できます。(コメントより頂きました。)
ディスカッション
コメント一覧
とても参考になりました。ありがとうございます!
練習問題の追加です。
https://atcoder.jp/contests/abc136/tasks/abc136_d
個人的な意見なのですが、↑の問題の場合
https://github.com/2357or/MyLibrary/blob/master/Libraries/RunLength%E5%9C%A7%E7%B8%AE/RunLength.cpp
のようにpair配列の方都合がいい場合があります。このような実装方法も載せるのはどうでしょうか?
練習問題の追加、実装方法の追加提案などありがとうございます!
記事に例として追加しました。
計算量
符号化:()
復元:()
もとの文字列のサイズをとすると、どちらも() で計算できます。
元の文字列のサイズをNとすると、
ですかね?誤植です