Isa@Diary

ソフトウェア開発やってます。プログラミングとか、US生活とかについて書きます。

リンゴ列をもっと短く!

三分木を使ってハフマン符号を構成すればよかった。
初めは出現頻度の低い方から3つをmergeしていけばよい、とか考えていたけど、
少し考えると初めにmergeするときに2つをmergeしないと最小にならない、という
ことに気づいた。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <string>

using namespace std;

map<char,string> symbols;
char rgb[] = {'r','g','b'};

class Leaf{
public:
    int cnt;
    vector<char> chars;

    Leaf(int _cnt, vector<char> _chars):cnt(_cnt),chars(_chars){}

    Leaf(){}

    bool operator <(const Leaf arg) const{
        return cnt < arg.cnt;
    }
};

int main(){
    string str;
    cin >> str;
    vector<int> times(26);
    vector<Leaf> leaves;    
    Leaf newLeaf, tmp;
    for(int i=0;i < (int)str.size();i++){
        times[str[i] - 'A']++;
    }

    for(int i=0;i<26;i++){
        leaves.push_back(Leaf(times[i], vector<char>(1, i+'A')));
    }

    while((int)leaves.size() > 1){
        sort(leaves.rbegin(), leaves.rend());
        newLeaf = Leaf(0, vector<char>());
        int loopCount = leaves.size() % 2 ? 3 : 2;
        cerr << "merge: {";
        for(int i=0;i<loopCount;i++){
            tmp = leaves.back();
            leaves.pop_back();
            newLeaf.cnt += tmp.cnt;
            for(int j=0;j<(int)tmp.chars.size();j++){
                cerr << " " << tmp.chars[j] << " ";
                symbols[tmp.chars[j]] += i+'0';
                newLeaf.chars.push_back(tmp.chars[j]);
            }
            if(i != loopCount - 1) cerr << "} and {";
        }
        cerr << "}" <<endl;
        leaves.push_back(newLeaf);
    }
    map<char,string>::iterator it = symbols.begin();

    while(it != symbols.end()){
        string str = (*it).second;
        cout << (*it).first << ":";
        for(int i=str.size();i>=0;i--){
            cout << rgb[str[i]-'0'];
        }
        cout << endl;
        ++it;
    }
}