C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で文字列リストをエンコード・デコードするアルゴリズムの実装方法


文字列のリストが与えられたとき、そのリスト全体を1つの文字列にエンコードするアルゴリズムを設計し、さらにエンコードされた文字列を元のリストへ復元(デコード)する仕組みを実装することを考えます。エンコーダーとデコーダーはそれぞれ別のマシンに配置されており、以下のような2つの関数として構成されます。

マシン1(送信側)が持つ関数

string encode(vector<string> strs) {
    // 文字列を読み込み、encoded_string を返す
}

マシン2(受信側)が持つ関数

vector<string> decode(string s) {
    // encoded_string をデコードし、strs を返す
}

例えば、入力が {"hello", "world", "coding", "challenge"} の場合、出力は次のようになります。

  • エンコード後の文字列: 5#hello5#world6#coding9#challenge
  • デコード後のリスト: [hello, world, coding, challenge]

解決のアプローチ

この問題は、各文字列を「長さ + 区切り文字(#) + 文字列本体」という形式で連結することで解決できます。この方式を採用すれば、文字列本体に「#」や数字が含まれていても、先頭の長さ情報に基づいて正確に境界を判定できるため、安全にエンコード・デコードが行えます。

具体的な手順は以下の通りです。

  • encode() 関数を定義し、文字列配列 strs を受け取ります。
  • ret := 空文字列 で初期化します。
  • i := 0 から strs のサイズ未満まで、i を1ずつ増やしながら繰り返します。
    • ret := ret + 「strs[i] のサイズ」+ "#" + strs[i]
  • ret を返します。
  • getNext() 関数を定義し、x(検索対象の文字)、start(検索開始位置)、s(対象文字列)を受け取ります。
  • idx := s のサイズ(見つからなかった場合の初期値)とします。
  • i := start から s のサイズ未満まで繰り返します。
    • s[i] が x と一致したら、idx := i としてループを抜けます。
  • idx を返します。
  • decode() メソッドを定義し、文字列 s を受け取ります。
  • 結果格納用の配列 ret を定義します。
  • i := 0、n := s のサイズ とします。
  • i < n の間、以下を繰り返します。
    • hashPos := getNext('#', i, s) — 次の「#」の位置を取得します。
    • len := s の i 番目から hashPos - i 文字分の部分文字列を整数に変換します(=文字列の長さ)。
    • i := hashPos + 1 と更新します。
    • s の i 番目から len 文字分の部分文字列を ret の末尾に追加します。
    • i := i + len と更新します。
  • ret を返します。

実装例

理解を深めるために、以下のC++による実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
    cout << "[";
    for(int i = 0; i<v.size(); i++){
        cout << v[i] << ", ";
    }
    cout << "]"<<endl;
}
class Codec {
public:
    string encode(vector<string>& strs) {
        string ret = "";
        for (int i = 0; i < strs.size(); i++) {
            ret += to_string(strs[i].size()) + "#" + strs[i];
        }
        return ret;
    }
    int getNext(char x, int start, string s){
        int idx = s.size();
        for (int i = start; i < s.size(); i++) {
            if (s[i] == x) {
                idx = i;
                break;
            }
        }
        return idx;
    }
    vector<string> decode(string s) {
        vector<string> ret;
        int i = 0;
        int n = s.size();
        while (i < n) {
            int hashPos = getNext('#', i, s);
            int len = stoi(s.substr(i, hashPos - i));
            i = hashPos + 1;
            ret.push_back(s.substr(i, len));
            i += len;
        }
        return ret;
    }
};
main(){
    Codec ob;
    vector<string> v = {"hello", "world", "coding", "challenge"};
    string enc = (ob.encode(v));
    cout << "Encoded String " << enc << endl;
    print_vector(ob.decode(enc));
}

入力

{"hello", "world", "coding", "challenge"}

出力

Encoded String 5#hello5#world6#coding9#challenge
[hello, world, coding, challenge]

このように「長さ#文字列」の形式で連結していくことで、空白文字や記号を含む文字列でも確実に元の状態へ復元できます。エンコード・デコードともに処理時間は全文字数に対して線形(O(n))となるため、効率的な手法と言えます。

  1. C++で2つの数値文字列を乗算し、結果を文字列として返すプログラム

    問題の概要2つの数値が文字列として与えられているとします。これらを乗算し、その結果も文字列として返す必要があります。例えば、入力が「28」と「25」であれば、出力は「700」になります。数値が非常に大きい場合、通常の int 型や long long 型では扱えないことがあります。そこで、文字列のまま筆算のように計算することで、どんなに大きな数でもオーバーフローせずに正確な積を求められるのが、この手法の大きな利点です。解決のための手順num1 の桁数を n、num2 の桁数を m とします。2数の積は最大 n + m 桁になるため、長さ n + m の文字列 ans を「0」で初期化します。n

  2. C++で2つの2進数文字列を加算するプログラムの書き方

    2つの2進数を表す文字列が与えられたとき、それらを加算した結果を求め、その結果を2進数の文字列として返すことを考えます。2進数とは、0か1のいずれかで表現される数値のことです。2進数同士を足し合わせる際には、以下のような2進数特有の加算ルールに従う必要があります。0+0 → 0 0+1 → 1 1+0 → 1 1+1 → 0(繰り上がり1)入力例str1 = {11}, str2 = {1}出力例100入力例str1 = {110}, str2 = {1}出力例111問題を解くためのアプローチ両方の文字列を末尾(最下位桁)から走査する対応する桁の2進数同士を加算する1と1を足した場合は、その桁