题目描述:
给定一段文字,如果我们统计出字母出现的频率,是可以根据哈夫曼算法给出一套编码,使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的。例如对字符串"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
大致意思就是:给你一些字符,以及字符出现次数,输入字符编码要你判断是不是哈夫曼编码。
解题思路:解读题意,即要你比较是不是案例编码是不是最优长度的前缀编码。那么就要判断两方面:
1、是最优长度编码(也就是WPL)。
2、符合前缀编码性质(即任何一个编码都不是其他某一个编码的前缀,比如案例四中,E的编码00正是A、B、C、D的前缀编码,这样在解码时会产生歧义,也就是结果不一)。
首先我们要求的是最优长度编码。
举个例子:构造A1 B2 C2 D5 E9的哈夫曼树以及求其WPL。
这便是构造出的哈夫曼树。WPL = (|A|+|B|)* 4 + |C| * 3 + |D| * 2 + |E| * 1 = 37。这个是使用权值乘以路径长度,但是在计算其中非叶子节点的权值时,3 包含了1个(1+2),5是用3得来的,所以也包含了一个(1+2),同理10和19也是一样,所以如果直接将所有非叶子节点相加
WPL = 3 + 5 + 10 + 19 = 37。
所以我们可以用优先队列处理,代码如下:
priority_queue<int , vector<int>,greater<int>> que;
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
{
char x;
int num;
cin >> x >> num;
q[x] = num;
que.push(num);
}
int ans = 0;
while(que.size() >= 2)
{
int a = que.top(); que.pop();
int b = que.top(); que.pop();
que.push(a+b);
ans += a+b;
}
}
这样求出的ans就是最优长度编码了。
接下来就是判断前缀编码了:
bool check(string s1,string s2)
{
int a = min(s1.size(),s2.size());
for(int i = 0;i < a;i++)
{
if(s1[i] != s2[i])
{
return true;
}
}
return false;
}
方法也很简单,就是逐个比较如果有不同的就满足了。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
map<char,int> q;
int data[1010];
vector<int> arr;
priority_queue<int , vector<int>,greater<int>> que;
bool check(string s1,string s2)
{
int a = min(s1.size(),s2.size());
for(int i = 0;i < a;i++)
{
if(s1[i] != s2[i])
{
return true;
}
}
return false;
}
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
{
char x;
int num;
cin >> x >> num;
q[x] = num;
que.push(num);
}
int ans = 0;
while(que.size() >= 2)
{
int a = que.top(); que.pop();
int b = que.top(); que.pop();
que.push(a+b);
ans += a+b;
}
//cout << ans << endl;
int tt;
cin >> tt;
while(tt--)
{
int cnt = 0;
string s[1010];
for(int i = 1;i <= n;i++)
{
char x;
cin >> x >> s[i];
cnt += q[x]*s[i].size();//求案例的编码长度
}
//check最优
if(cnt != ans)
{
arr.push_back(0);
continue;
}
//check是否满足不是前缀编码
int flag = 0;
for(int i = 1;i <= n;i++)
{
for(int j = i+1;j <= n;j++)
{
if(!check(s[i],s[j]))
{
arr.push_back(0);
flag = 1;
break;
}
}
if(flag)
{
break;
}
}
if(!flag)
{
arr.push_back(1);
}
}
for(int i = 0;i < arr.size();i++)
{
if(arr[i])
{
cout << "Yes" << endl;
}
else{
cout << "No" << endl;
}
}
return 0;
}
同样将判断结果先放入vector容器,最后统一输出。