请帮我用C语言写一段程序,要求使用贪心算法思想:给定26个1~100表示的非归一化概率,和一个26个小写字母组成的序列,计算使用霍夫曼压缩后,这个序列占用的总比特数。输入为一行,前面26个数字为小写字母a-z的非归一化概率,然后是一个序列,数字和序列间均以空格隔开。输出为一个数字,表示这个序列占用的总比特数。此外,要求:压缩后的序列不考虑字节补齐(即不需要变成字节的整倍数);输出为总比特数对100007取模;概率范围在1~100,字符串长度100000~1000000。(当输入为“18 40 57 51 44 35 16 79 90 67 61 15 84 8 16 24 42 63 31 82 65 1 21 17 67 81 toyjzhiwzj”时,输出为“44”。)
时间: 2024-03-18 17:42:44 浏览: 198
以下是使用贪心算法思想,用C语言实现的程序:
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 1000005
#define MOD 100007
typedef struct {
int val;
int freq;
} Node;
Node heap[MAX_LEN];
int heap_size;
void swap(Node *a, Node *b) {
Node tmp = *a;
*a = *b;
*b = tmp;
}
void push(Node node) {
heap[++heap_size] = node;
int i = heap_size;
while (i > 1 && heap[i].val < heap[i / 2].val) {
swap(&heap[i], &heap[i / 2]);
i /= 2;
}
}
Node pop() {
Node ret = heap[1];
heap[1] = heap[heap_size--];
int i = 1;
while (i * 2 <= heap_size) {
int child = i * 2;
if (child < heap_size && heap[child + 1].val < heap[child].val) {
child++;
}
if (heap[i].val > heap[child].val) {
swap(&heap[i], &heap[child]);
i = child;
} else {
break;
}
}
return ret;
}
int main() {
int freq[26];
memset(freq, 0, sizeof(freq));
for (int i = 0; i < 26; i++) {
scanf("%d", &freq[i]);
push((Node){freq[i], 1});
}
char s[MAX_LEN];
scanf("%s", s);
int len = strlen(s);
for (int i = 0; i < len; i++) {
int idx = s[i] - 'a';
freq[idx]++;
push((Node){freq[idx], 1});
}
while (heap_size > 1) {
Node a = pop();
Node b = pop();
push((Node){a.val + b.val, a.freq + b.freq});
}
Node root = pop();
int ans = 0;
for (int i = 0; i < 26; i++) {
int code_len = 0;
int code = 0;
if (freq[i] > 0) {
Node node = heap[1];
while (node.freq > 1 || node.val != freq[i]) {
if (node.freq > 1) {
code_len++;
code = (code << 1) | 1;
node.freq--;
push(node);
} else {
code_len++;
code <<= 1;
node = pop();
}
}
ans += code_len * freq[i];
ans %= MOD;
}
}
printf("%d", ans);
return 0;
}
```
程序的思路是:首先读入26个小写字母的概率,构建哈夫曼树。然后读入需要压缩的序列,根据序列中每个字母的出现次数,计算出其对应的哈夫曼编码,最后统计所有字母的编码长度乘以出现次数,得到压缩后的总比特数。
具体实现中,使用一个小根堆来维护哈夫曼树的构建过程,每次取出两个权值最小的节点作为左右子节点,构成一棵新的子树,再将其加入小根堆中。在计算哈夫曼编码时,使用一个循环来模拟编码的过程,具体来说,如果当前节点的频率大于1,则将其标记为1,并将其重新加入小根堆中;如果当前节点的频率为1,且其权值不等于当前字母的出现次数,则说明其是其他字母的父节点,继续取出下一个节点;如果当前节点的频率为1,且其权值等于当前字母的出现次数,则说明已经找到了当前字母的编码,统计编码长度并计算总比特数。
最后,将总比特数对100007取模并输出即可。
阅读全文