第五届“图灵杯”NEUQ-ACM程序设计大赛 F题 字符串处理哈夫曼树

题目描述:
寂寞的PJ终于找到了一个超级可爱的女朋友,他想给她写一封情书,但是他文笔不好,情商又低,只能写写代码才能维持的了生活这个样子 正好PJ可爱的女朋友也是学计算机的,她刚刚学完《计算机导论》,于是PJ灵机一动,打算用二进制写一封情书给她。 因为PJ凑不出来字数,于是PJ想到了这么一种编码方式来延长自己的情书长度:
1、先用英语写好情书,记录出现的英文字符的种类n(0< n <=52)区分大小写。
2、记录每种英文字符出现的频数,按从大到小的次序排好放入队列。如果频数相同,则按字典序。 3、将当前队列中的队首两元素分别作为左右子节点(频数较高的作为左子节点)创建一个父节点,父节点自身的频数值为左右节点的和。并将该父节点放回到队首。 4、重复上述行为直到队列中所有的元素都已经添加到同一棵二叉树上。 5、从根节点开始以深度优先的方式遍历节点,每个节点的左树枝边值记为0,右树枝边值记为1,直到叶节点,按顺序读取树枝的值便是我们所要的该字符的编码,我们称之为PJ编码。 值得注意的是,PJ希望他的情书可读性更强,所以他只转换英文字符,其他字符都不变。并且在输出情书正文之前他要先给他可爱的女孩一个字典以便读懂这篇情书。 情书的长度不超过600字符
输入:
以文件结尾为结束,输入一整篇英文情书,包括空格和回车。
输出:
前n行以从出现频数高到低输出已经出现的英文字符和它对应的PJ编码,以英文冒号和空格隔开。 接下来按原来的格式输出已经转换成PJ编码形式的情书。
样例输入
I AM PJ
I Love you so much!
CC
样例输出
o: 000000000000000
C: 000000000000001
I: 00000000000001
u: 0000000000001
A: 000000000001
J: 00000000001
L: 0000000001
M: 000000001
P: 00000001
c: 0000001
e: 000001
h: 00001
m: 0001
s: 001
v: 01
y: 1
00000000000001 000000000001000000001 0000000100000000001
00000000000001 000000000100000000000000001000001 10000000000000000000000000001 001000000000000000 00010000000000001000000100001
000000000000001000000000000001
注意:错误:程序中有游离的‘\243’ 说明有中文输入的字符
这道题是利用哈夫曼树编写哈夫曼编码的一个应用,不过相较于原版的哈夫曼编码,一个最大的改变就是从“使出现频率最高的字符拥有最短的编码”变为了“使出现频率最低的字符拥有最短的编码”。要获得这种改编的哈夫曼编码的话,就需要用到深度优先搜索DFS找到叶子节点,并在DFS途中用字符串记
录下经过的边。找到叶子节点后放在数组⾥保存,输出时逐一替换即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set> 

using namespace std;
struct node{
    node *lson;
    node *rson;
    char alpha;
    int weight;//出现的次数作为权重 
    string code; 
};
string haff[52]; //用来记录每个字母最后的编码 
int a[52];//用来记录每个字母出现的次数 
struct cmp
{
bool operator () (node* a,node* b)
{
if(a->weight!=b->weight)
return a->weight>b->weight;
else return a->alpha<b->alpha;
}
};
set<node*,cmp> ss;
string s;
void dfs(node *a);
int main()
{
    char c;
    //记录原情书,最后还要按照此顺序输出 
    while((c=getchar())!=EOF)
    {
        s += c;
        if(c>='a'&&c<='z')a[c-'a']++; 
        if(c>='A'&&c<='Z')a[c-'A'+26]++; 
    }
    //对于每一个出现的字母生成结点插入set中 
    for(int i = 0;i<52;i++)
    {
        if(a[i]!=0)
        {
            node* temp = new node;
            temp->weight = a[i];
            if(i>=0&&i<=25) temp->alpha = i+'a';
            else if(i>=26&&i<=51) temp->alpha = i-26+'A'; 
            temp->lson = NULL;
            temp->rson = NULL;
            ss.insert(temp);
        }
    }
    //遍历整个set 生成哈夫曼树
    node* temp;
    for(set<node*,cmp>::iterator it = ss.begin();it!=ss.end();it++) 
    {
        if(it==ss.begin())
            temp = *it;
        else
        {
            node *tem = new node;
            tem->lson = temp;
            tem->rson = *it;
            tem->weight = temp->weight + (*it)->weight;//->的优先级比*高 注意加括号 
            temp = tem;
            //ss.insert(temp);//可以不用插入 因为两个最大的weight的和一定也是最大,下一次一定会被取出 
        }       

    }
    //最后temp指向根节点 
    dfs(temp);
    //按照情书的顺序输出 
    for(int i = 0;i<s.length();i++)
    {
        if(s[i]>='a'&&s[i]<='z')
        //  printf("%s",haff[s[i]-'a']);//出错%s是char[]的输出输入格式,不能用于string cannot pass objects of non-trivially-copyable type ‘std::string {aka class std::basic_string<char>}’ through ‘...’  
        cout<<haff[s[i]-'a'];
        else if(s[i]>='A'&&s[i]<='Z')
        //  printf("%s",haff[s[i]-'A'+26]);
        cout<<haff[s[i]-'A'+26];
        else cout<<s[i];
    } 
    return 0; 
}
void dfs(node *a)
{
    if(a->lson==NULL&&a->rson==NULL)
    {
        if(a->alpha>='a'&&a->alpha<='z')haff[a->alpha-'a'] = a->code;
        else haff[a->alpha-'A'+26] = a->code;
        cout<<a->alpha<<": "<<a->code<<endl; 
    }
    if(a->lson!=NULL)
    {
    //  a->lson->code+='0';//注意不是a->code
        a->lson->code = a->code + '0';
        dfs(a->lson); 
    }
    if(a->rson!=NULL)
    {
    //  a->rson->code +='1';
        a->rson->code = a->code + '1'; 
        dfs(a->rson); 
    }
}

第二种找规律的办法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
struct node{
    char alpha;
    int weight;
    string code;
};
struct cmp
{
    bool operator () (node *a ,node *b)
    {
        if(a->weight!=b->weight)
            return a->weight>b->weight;
        else return a->alpha<b->alpha; 
    }
};
set<node*,cmp> ss;
string s;
int a[52];
string haff[52];
int main()
{
    int cnt = 0;
    char c;
    //读入字符串并计数 
    while((c=getchar())!=EOF)
    {
        s+=c;
        if(c>='a'&&c<='z')a[c-'a']++;
        else if(c>='A'&&c<='Z')a[c-'A'+26]++;
    }
    for(int i = 0;i<52;i++)
    {
        if(a[i]!=0)
        {
            cnt++;
            node* temp = new node;
            temp->weight = a[i];
            if(i<26)temp->alpha = i+'a';
            else temp->alpha = i+'A'-26;
            ss.insert(temp);
        }

    }
    for(set<node*,cmp>::iterator it=ss.begin();it!=ss.end();it++)
    {
        for(int i = 0;i<cnt-1;i++)
        {
            (*it)->code+='0';
        }
        if(it!=ss.begin())
            (*it)->code+='1';
        cnt--;
        char cc = (*it)->alpha;
        if(cc>='a'&&cc<='z') haff[cc-'a']=(*it)->code;
        else haff[cc-'A'+26] = (*it)->code;
        cout<<cc<<": "<<(*it)->code<<endl; 
    }
    for(int i = 0;i<s.length();i++)
    {
        if(s[i]>='a'&&s[i]<='z') cout<<haff[s[i]-'a'];
        else if(s[i]>='A'&&s[i]<='Z') cout<<haff[s[i]-'A'+26];
        else cout<<s[i];
    }
    return 0;
} 

第三种找规律 用map映射(lx所写)

#include <iostream>
#include <ctype.h>
#include <algorithm> 
#include <map>
#include <cmath>
using namespace std;

struct Char{

    char c;
    int  count;
    string text;
}array[150];

bool cmp(Char a,Char b)
{
    if( a.count > b.count)
        return true;
    else if( a.count == b.count)
    {
        //a > b
        int ans = int(a.c)-int(b.c);
        //是同一个类型的字母 
        if( abs(ans) <= 26 ){

            if(a.c < b.c)
                return true; 
        }
        else{
            //a小b大 
            if( a.c < b.c)
                return true; 
        }
    }
    return false;   
}

int main()
{
    int i,j;
    int n;
    char c;
    char str[100001];
    map<char,string> m;
    int len = 0;
    for(i = 0; i < 150; i++ ){

        array[i].c = '0';
        array[i].count = 0;
        array[i].text = "";
    }
    while((c=getchar())!=EOF){

        str[len++] = c;
        if(isalpha(c)){

            array[(int)c].c = c;
            array[(int)c].count++;
        }
    }
    str[len] = '\0';    

    sort(array,array+150,cmp);
    for( i = 0; ; i++ ){        
        if(array[i].count == 0 )
            break;
        n++;
    }
    for( i = 0; i < n ; i++ ){      

        if( i == 0){

            for( j = 0; j < n-1-i;j++)
                array[i].text += "0";
        }
        else{
            for( j = 0; j < n-1-i;j++)
                array[i].text += "0";
            array[i].text += "1";       
        }
        m[array[i].c] = array[i].text;
        cout << array[i].c << ":" << array[i].text << endl;
    }
//  
    for( i = 0; i < len; i++ ){

        if( isalpha(str[i])){

            cout << m[str[i]];
        }
        else{       
            putchar(str[i]);
        }

    }

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值