PTA哈夫曼編碼 (30分)

PTA哈夫曼編碼 (30分)

給定一段文字,如果我們統計出字母出現的頻率,是可以根據哈夫曼算法給出一套編碼,使得用此編碼壓縮原文可以得到最短的編碼總長。然而哈夫曼編碼並不是唯一的。例如對字符串"aaaxuaxz",容易得到字母 ‘a’、‘x’、‘u’、‘z’ 的出現頻率對應爲 4、2、1、1。我們可以設計編碼 {‘a’=0, ‘x’=10, ‘u’=110, ‘z’=111},也可以用另一套 {‘a’=1, ‘x’=01, ‘u’=001, ‘z’=000},還可以用 {‘a’=0, ‘x’=11, ‘u’=100, ‘z’=101},三套編碼都可以把原文壓縮到 14 個字節。但是 {‘a’=0, ‘x’=01, ‘u’=011, ‘z’=001} 就不是哈夫曼編碼,因爲用這套編碼壓縮得到 00001011001001 後,解碼的結果不唯一,“aaaxuaxz” 和 “aazuaxax” 都可以對應解碼的結果。本題就請你判斷任一套編碼是否哈夫曼編碼。

輸入格式:
首先第一行給出一個正整數 N(2≤N≤63),隨後第二行給出 N 個不重複的字符及其出現頻率,格式如下:

c[1] f[1] c[2] f[2] … c[N] f[N]

其中c[i]是集合{‘0’ - ‘9’, ‘a’ - ‘z’, ‘A’ - ‘Z’, ‘_’}中的字符;f[i]是c[i]的出現頻率,爲不超過 1000 的整數。再下一行給出一個正整數 M(≤1000),隨後是 M 套待檢的編碼。每套編碼佔 N 行,格式爲:

c[i] code[i]

其中c[i]是第i個字符;code[i]是不超過63個’0’和’1’的非空字符串。

輸出格式:
對每套待檢編碼,如果是正確的哈夫曼編碼,就在一行中輸出"Yes",否則輸出"No"。

注意:最優編碼並不一定通過哈夫曼算法得到。任何能壓縮到最優長度的前綴編碼都應被判爲正確。

輸入樣例:
7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11

輸出樣例:
Yes
Yes
No
No

一開始看到這道題的時候覺得無從下手,然後就去複習了一下哈夫曼樹和哈夫曼編碼,看完之後也是沒有很好地思路,但是我注意到了哈夫曼編碼的兩個性質:

  • 哈夫曼編碼可能不唯一,但是哈夫曼編碼的長度是唯一的。字符串編碼成01串後的長度實際上就是其以頻率爲權值所構成的任意一顆哈夫曼樹的帶權路徑長度。
  • 對於任何一個葉子結點,其編號一定不會成爲其他任何一個結點編號的前綴—也就是說,題目中給出需要判斷的的每個字符的編碼,它不會是其他字符編碼的前綴

知道這兩個性質之後,我就決定抓住這兩點去判斷所給出的編碼方式是否爲正確的哈夫曼編碼。也是抱着試試的心態,沒想到一遍就AC了。所以我就認爲這道題考查的其實就是哈夫曼編碼的性質吧。。。
在這裏插入圖片描述
AC代碼如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
//代表小頂堆的優先隊列
priority_queue<int ,vector<int>,greater<int> > q;
int main(void){
	int n;
	char s[66];
	int p[66];
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		getchar();
		scanf("%c%d",&s[i],&p[i]);
		q.push(p[i]);
	}
	int ans=0;
	//ans即爲哈弗曼樹帶權路徑長度和,也就是所對應的哈夫曼編碼的長度
	while(q.size()>1){
		int x=q.top();
		q.pop();
		int y=q.top();
		q.pop();
		q.push(x+y);
		ans+=x+y;
	}
	int m;
	scanf("%d",&m);
	for(int i=0;i<m;i++){
		char ss[66][66];
		int sum=0;
		for(int j=0;j<n;j++){
			getchar();
			char c;
			scanf("%c %s",&c,ss[j]);
			sum+=strlen(ss[j])*p[j];
		}
		if(sum!=ans)printf("No\n");
		else{
			int flag=0;
			for(int j=0;j<n;j++){
				int x=strlen(ss[j]);
				for(int l=0;l<n;l++){
					if(j!=l){
						if(strstr(ss[l],ss[j])==&ss[l][0]){
						//查找字符串,如果找到了並且是前綴,就標記爲No了
							flag=1;
							break;
						}
					}
				}
				if(flag==1)break;
			}
			if(flag==1)printf("No\n");
			else printf("Yes\n");
		}
	}
	return 0;
}

這道題能這麼快通過我也是很意外,可能中間就是我想多了吧,不過哈夫曼編碼的這兩條性質確實很重要哦~

發佈了7 篇原創文章 · 獲贊 16 · 訪問量 761
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章