108年彰雲嘉下午P10 - Huffman

題目:

Huffman : 維基百科

霍夫曼樹經常被用來作資料壓縮,將字串資料轉成霍夫曼碼以減少字串資料所佔的空間大小,其詳細作法如下:
	(1)字串中所有的字母元素都放在最底層的葉節點上,節點中的值為此字母的出現次數。
	(2) 每次從尚無父節點的所有節點中,尋找值最小(出現次數最少)的兩個節點來建立霍夫曼樹,
	產生其父節點的值為兩個節點值相加的和。最後只剩下根節點沒有父節點。
	(3) 從根節點開始往下編碼,左分支為 0,右分支為 1,每個葉節點的字母元素會產生一個霍夫
	曼碼(由 0 和 1 所組成),字母元素出現的頻率越高則霍夫曼碼越短。
	
我們這題需要計算一個字串轉成霍夫曼碼的壓縮率。假設有個字串"queue",則此字串會佔5 bytes = 40bits空間,
轉成霍夫曼碼後應為"00011011",只需要8個bits就可儲存,因此壓縮率為(40-8)/40 * 100% = 80%。

輸入:

輸入一列字串(長度最多100字元,且不含空格)。

輸出:

依序輸出此字串壓縮前的bit數、壓縮後的bit數、壓縮率百分比(格式為⌊n⌋%,即n向下取整)。

Ex 1 Input:

queue

Ex 2 Input:

abadcafcabfdec

Ex 3 Input:

aaaaaaamcmhhhhhmkkkkkkkkmbbmdddddddmyymppppp

Ex 4 Input:

BCBBB

Ex 1 Output:

40 8 80%

Ex 2 Output:

112 35 68%

Ex 3 Output:

352 132 62%

Ex 4 Output:

40 5 87%

題解:

題目沒說字串中大小寫是否要算成同一字母,因此先視為不同的來處理。

霍夫曼編碼是出現頻率越多次的編碼越短,而越少就越長,
可以利用priority_queue(優先佇列)對字母出現的頻率進行排序,
而建立Huffman Tree只要依序拿取優先佇列中的值並相加就好,

字串原bit數就是字串長度*8,因為1bytes = 8bits。
壓縮後bit數就是一直疊加自己與父節點的值就是答案。

上程式碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#pragma GCC optimize("Ofast")
#pragma loop_opt(on)
#include<bits/stdc++.h>
using namespace std;
template<class T> long long Mod(T a,T b){return ((a%b)+b)%b;}
#define endl '\n'
#define ll long long
#define IO ios_base::sync_with_stdio(0); cin.tie(0);
#define GETOUT cout.tie(0);
#define gc getchar()
#define cendl cout << endl;
#define fr(bob,n,l) for(int bob=(n);bob<(l);++bob)
#define fra(ns,a) for(auto (ns):a)
#define frc(bot,ns,l) for(int bot=(ns);bot<=(l);++bot)
#define frx(i,ns,l) for(int i=(ns);i<(l);++i)
const int MAX=1e9+7;
const int MOD=998244353;
inline int nextint(){
	int x=0,w=1;char ch=0;
  	while(ch<'0'||ch>'9'){
    	if(ch == '-')w=-1;
    	ch=getchar();
  	}
  	while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=getchar();
  	return x*w;
}
struct huff{
	int feq;
	char key;
};
struct cmp{
	bool operator()(huff i,huff j){
	return i.feq > j.feq;
	}
};
inline void solve(){
	string s;
	cin >> s;
	int len = s.size();
	int bitlen = len*8;
	int feqs[1000]={0};
	priority_queue<huff,vector<huff>,cmp> huffman;
	for(int i=0;i<len;i++){
		feqs[s[i]]++;
	}
	for(int i=0;i<1000;i++){
		if(feqs[i]!=0){
			huff h;
			h.feq = feqs[i];
			char k = i;
			h.key = k;
			huffman.push(h);
		}
	}
	int ans=0;
	while(huffman.size()>=2){
		huff now = huffman.top();huffman.pop();
		huff next = huffman.top();huffman.pop();
		huff cal;cal.feq = now.feq + next.feq;
		huffman.push(cal);
		ans+=cal.feq;
	}
	double rate = (bitlen-ans)/(double)bitlen;
	cout << bitlen << ' ' << ans << ' '  << floor(rate*100) << '%' << endl;
}
signed main(){
	IO;
	GETOUT;
	int t;
	t=1;
	while(t--)solve();
	return 0;
}