在课堂上,我们学习了哈夫曼编码的原理和实现方法,上实验课时也动手实现过,后来我们又追加介绍了哈夫曼编码的实际压缩和解压缩的实现方法,并且在课堂上也演示了,但当时我们却忽略了一个环节,那就是实际文件存储时,二进制是比特位,而存储的单位一般是字节,显示时又是按照十六进制的。现在给你一个由字典里的字符组成的原文,用哈夫曼方法把该原文压缩成十六进制码。
Input
本问题有多组测试数据,第一行就是测试数据的组数nCase,对于每组测试数据,一共有四个部分,第一部分是一个字典(请注意,字典里可能含有空格!),原文本里面出现的任何字符一定在这个字典里面,并且已经按照使用频度从大到小顺序排列。第二部分是字典里相对应字符的使用频度。第三部分是原文的行数n。第四部分是n行原文。
Output
输出一共n行,每行就是原文对应的十六进制压缩码。
特别说明:
1:由于哈夫曼编码可能不一定唯一,因此我们规定在构建哈夫曼树时,左子树编码为0,右子树编码为1,左子树代表的频度数据小于右子树代表的频度数据,如果两个频度数据相同,则以生成早的为左子树,如果在字典里出现相同频度的字符,则原排在前的为左子树。这样规定目的是确保哈夫曼编码唯一。
2:如果在压缩过程中,用哈夫曼方法压缩后二进制码的长度不是8的倍数,在码的最后添数字‘0’使其长度成为8的倍数(注意最多添7个‘0’)。
Sample Input
1 AORST 60 22 16 13 6 4 3 AO ASAO RST ATOAATS OSAATRR RRASTO STROAR SSRTOAAA
Sample Output
7C F3F2CC3C6FE24D3FC5AB7CC6 98BBD266C6FF80
下面是实现压缩的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
struct TREE{
int l,r;
int now,w;
string str;
TREE(){};
TREE(int now,int w,string str)
{
this->now=now;
this->w=w;
this->str=str;
this->l=this->r=-1;
}
TREE(int now,int w,string str,int l,int r)
{
this->now=now;
this->w=w;
this->str=str;
this->l=l;
this->r=r;
}
friend bool operator<(TREE a,TREE b)
{
if(a.w!=b.w) return a.w>b.w;
else return a.now>b.now;
}
};
int n,x,head;
string key,trans;
vector<TREE> tree;
priority_queue<TREE> q;
vector<bool> binary;
char hexa[20]="0123456789ABCDEF";
void buildTree()
{
while(q.size()>1)
{
TREE tmp1=q.top();
q.pop();
TREE tmp2=q.top();
q.pop();
tree.push_back(TREE(tree.size(),tmp1.w+tmp2.w,tmp1.str+tmp2.str,tmp1.now,tmp2.now));
q.push(tree[tree.size()-1]);
}
head=q.top().now;
q.pop();
}
void toBinary(int t,string s)
{
if(s==tree[t].str)
return;
int lc=tree[t].l;
int rc=tree[t].r;
if(lc==-1||rc==-1)
return;
string tmp=tree[lc].str;
if(tmp.find(s,0)!=string::npos)
{
binary.push_back(0);
toBinary(lc,s);
}
else
{
binary.push_back(1);
toBinary(rc,s);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
cin.get();
while(T--)
{
getline(cin,key);
for(int i=0;i<key.length();i++)
{
cin>>x;
tree.push_back(TREE(i,x,key.substr(i,1)));
q.push(tree[i]);
}
buildTree();
cin>>n;
cin.get();
while(n--)
{
getline(cin,trans);
for(int i=0;i<trans.length();i++)
toBinary(head,trans.substr(i,1));
while(binary.size()%8)
binary.push_back(0);
for(int i=0;i<binary.size();i+=4)
{
int tmp=0,k=1;
for(int j=3;j>=0;j--,k*=2)
if(binary[i+j]) tmp+=k;
cout<<hexa[tmp];
}
cout<<endl;
binary.clear();
}
tree.clear();
}
}
在课堂上,我们学习了哈夫曼编码的原理和实现方法,上实验课时也动手实现过,后来我们又追加介绍了哈夫曼编码的实际压缩和解压缩的实现方法,并且在课堂上也演示了,但当时我们却忽略了一个环节,那就是实际文件存储时,二进制是比特位,而存储的单位一般是字节,显示时又是按照十六进制的。现在给你一个已经用哈夫曼方法压缩过的十六进制文件,请你解压以便还原成原文。
Input
本问题有多组测试数据,第一行就是测试数据的组数nCase,对于每组测试数据,一共有四个部分,第一部分是一个字典(请注意,字典里可能含有空格!),原文本里面出现的任何字符一定在这个字典里面,并且已经按照使用频度从大到小顺序排列。第二部分是字典里相对应字符的使用频度。第三部分是待解压的行数n。第四部分是n行经过哈夫曼压缩的十六进制数组成的字符串。
Output
输出一共n行,每行就是对应输入的原文(请注意,输出的原文里可能含有空格!)。
Sample Input
1 AORST 60 22 16 13 6 4 5 7C F3F2CC3C6FE24D3FC5AB7CC6 98BBD266C6FF81 FE6517F5B6663AF98FE2226676FA80 F317262FCFE662FC99D7D
Sample Output
AO ASAO RST ATOAATS OSAATRR RRASTO STROAR SSRTOAAA ||Error ! AASS TRAA RRRSSTA RASTAATTTSSSOOAOR ASTRO STRAO AASSTRAO SSORAR
Hint
1:由于哈夫曼编码可能不一定唯一,因此我们规定在构建哈夫曼树时,左子树编码为0,右子树编码为1,左子树代表的频度数据小于右子树代表的频度数据,如果两个频度数据相同,则以生成早的为左子树,如果在字典里出现相同频度的字符,则原排在前的为左子树。这样规定目的是确保哈夫曼编码唯一。
2:如果在解压缩过程中,找不到对应的原文,那么在遇到错误后加上字符串“||Error !”,并停止对压缩串的解压。
下面是解压的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
struct TREE{
int l,r;
int now,w;
string str;
TREE(){};
TREE(int now,int w,string str)
{
this->now=now;
this->w=w;
this->str=str;
this->l=this->r=-1;
}
TREE(int now,int w,string str,int l,int r)
{
this->now=now;
this->w=w;
this->str=str;
this->l=l;
this->r=r;
}
friend bool operator<(TREE a,TREE b)
{
if(a.w!=b.w) return a.w>b.w;
else return a.now>b.now;
}
};
int n,x,head;
string key,trans;
vector<TREE> tree;
priority_queue<TREE> q;
queue<bool> binary;
map<char,int> hexa;
void buildTree()
{
while(q.size()>1)
{
TREE tmp1=q.top();
q.pop();
TREE tmp2=q.top();
q.pop();
tree.push_back(TREE(tree.size(),tmp1.w+tmp2.w,tmp1.str+tmp2.str,tmp1.now,tmp2.now));
q.push(tree[tree.size()-1]);
}
head=q.top().now;
q.pop();
}
void toOrigin(int t)
{
int lc=tree[t].l;
int rc=tree[t].r;
if(lc==-1||rc==-1)
{
cout<<tree[t].str;
return;
}
if(binary.empty())
{
cout<<"||Error !";
return;
}
if(!binary.front())
{
binary.pop();
toOrigin(lc);
}
else
{
binary.pop();
toOrigin(rc);
}
}
void init()
{
for(int i=0;i<10;i++)
hexa['0'+i]=i;
for(int i=0;i<6;i++)
hexa['A'+i]=10+i;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
init();
cin>>T;
cin.get();
while(T--)
{
getline(cin,key);
for(int i=0;i<key.length();i++)
{
cin>>x;
tree.push_back(TREE(i,x,key.substr(i,1)));
q.push(tree[i]);
}
buildTree();
cin>>n;
cin.get();
while(n--)
{
getline(cin,trans);
for(int i=0;i<trans.size();i++)
{
int tmp=hexa[trans[i]];
int k=8;
for(int j=0;j<4;j++)
{
binary.push(tmp/k);
tmp%=k;
k>>=1;
}
}
while(!binary.empty())
{
toOrigin(head);
}
cout<<endl;
}
tree.clear();
}
}