#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020921"); }
题意:
输出字符串的长度*8、huffman编码长度、两者比值。
题解:
huffman编码:
我们发现对于一个字符串,如果我们把它变成01串,比如ABCDE
那么我们需要
A : 000
B : 001
C : 010
D : 100
E : 101
来表示每一个字符,然后识别的时候就是每三个一识别。
这种编码叫定长编码。
显然对于一个串,它的定长编码长度是串长*定长。
但是其实它可以更短。
如果我们想要识别一个串,就需要所有字符的编码都不为其它编码的前缀,这样才可以成功解码。
而此时我们可以根据出现频率来构造huffman编码,以达到最优解。
构造方法可以见于黑书P245,毕竟它还是很水的,随便看看就能明白。
下面是我要讲的方法:
这种编码最后可以构成一颗二叉树,每个叶子节点都是一种字符,而它的变长编码(Huffman)就是从root到这个节点经过的边的序号序列,而左儿子边序号标0,右儿子边序号标1。
建树方法:先把所有字符按照出现次数作为关键字排序,然后给最小的两个赋一个父亲,然后把这两个叶子节点弹出优先队列,把父亲插入进去,父亲的关键字是儿子的size之和。
最后只剩下一个节点的时候停止。
证明在黑书上有,自己去翻吧,感性的思考一下就是每个点都有深度,然后∑深度*size=ans,
我们需要让size大的深度尽量小。
呃,虽然上面的证明显然是在chedan,但是,,并没有但是。
代码:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 1000 using namespace std; char s[N]; int cnt[N]; priority_queue<int>q; int ans,len; int main() { freopen("test.in","r",stdin); int i,j,k; while(scanf("%s",s)!=EOF) { while(!q.empty())q.pop(); len=strlen(s),ans=0; if(len==3&&s[0]=='E'&&s[1]=='N'&&s[2]=='D')return 0; memset(cnt,0,sizeof cnt); for(i=0;s[i];i++)cnt[s[i]]++; for(i=0;i<N;i++)if(cnt[i])q.push(-cnt[i]); for(;;) { i=q.top(),q.pop(); if(q.empty())break; j=q.top(),q.pop(); ans-=(i+j); q.push(i+j); } if(!ans)ans=len; printf("%d %d %.1lf\n",len*8,ans,(double)len*8/(double)ans); } return 0; }
时间: 2024-11-10 11:32:56