当前位置:首页 > GESP > 洛谷B3870题:位操作与二进制转换解决变长编码

洛谷B3870题:位操作与二进制转换解决变长编码

2个月前 (08-28)

洛谷B3870题:位操作与二进制转换解决变长编码 洛谷题解 进制转换 位运算 C++ GESP GESP四级 第1张

一、题目解读

洛谷B3870题要求将给定的无符号长整型数转换为可变长度的字节编码,并输出其十六进制表示。题目核心在于设计一种高效的编码方案,需处理数字的二进制位分组、最高位标记及字节顺序反转等细节。该问题考察对二进制位操作的理解与算法逻辑的严谨性,尤其在边界条件(如0的特殊编码)与分组补位机制上的实现。

二、解题思路

1. 二进制转换与去前导0:自定义toBinaryString函数将数字转为二进制串,通过倒序取余法生成,并去除前导0(避免无效位)。

2. 分组与最高位标记:将二进制串按7位分组,不足位补0。通过makeByte函数生成字节,非最后一组设置最高位为1,区分数据段与结束标志。

3. 字节反转输出:因处理从低位开始,最终需反转字节顺序,确保正确解码顺序。

4. 边界处理:0的特殊情况单独编码为单字节0,避免空输出。

三、解题步骤

1. 输入数字N:通过cin获取用户输入的无符号长整型数。

2. 二进制转换:调用toBinaryString(N)生成二进制串,去除前导0。

3. 分组编码:

○ 从末尾开始,每次取最长7位子串(不足补0)。

○ 通过makeByte将子串转为字节,根据位置标记最高位。

○ 存入bytes容器,记录编码结果。

4. 字节反转:使用reverse函数反转字节顺序,确保高位在前。

5. 输出十六进制:循环遍历字节,按格式%02X输出,空格分隔。

四、代码与注释

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 将数字转换为二进制字符串(去除前导0)
string toBinaryString(unsigned long long n) {
    if (n == 0) return "0"; // 0的特殊处理
    string binary;
    while (n > 0) {
        binary += (n % 2) + '0'; // 倒序取余生成二进制
        n /= 2;
    }
    reverse(binary.begin(), binary.end()); // 反转成正确顺序
    return binary;
}

// 将7位二进制字符串转换为字节(添加最高位)
char makeByte(const string& bits, bool isLast) {
    char byte = 0;
    for (int i = 0; i < 7; ++i) {
        char bit = (i < bits.size())? bits[i] - '0' : 0; // 不足位补0
        byte |= bit << (6 - i); // 按位左移拼接
    }
    if (!isLast) byte |= 1 << 7; // 非末尾组最高位设为1
    return byte;
}

// 主处理函数
vector<char> encodeNumber(unsigned long long n) {
    vector<char> bytes;
    if (n == 0) {
        bytes.push_back(0); // 0编码为单字节0
        return bytes;
    }
    
    string binary = toBinaryString(n);
    int len = binary.length();
    int pos = len;
    
    while (pos > 0) {
        int start = max(0, pos - 7); // 从末尾开始取7位组
        string group = binary.substr(start, pos - start);
        pos = start;
        
        // 补足7位
        while (group.length() < 7) {
            group = "0" + group;
        }
        
        bool isLast = (pos == 0);
        bytes.push_back(makeByte(group, isLast));
    }
    
    // 反转字节顺序(因处理从低位开始)
    reverse(bytes.begin(), bytes.end());
    return bytes;
}

int main() {
    unsigned long long N;
    cin >> N;
    
    vector<char> encoded = encodeNumber(N);
    
    // 输出十六进制表示
    for (size_t i =  encoded.size(); i >0; i--) {
        if (i!= encoded.size()) cout << " ";
        printf("%02X", (unsigned char)encoded[i-1]); // 强制类型转换避免警告
    }
    cout << endl;
    
    return 0;
}

五、总结

该解法通过模块化函数(二进制转换、字节生成、主逻辑)实现清晰编码流程,兼顾效率与边界处理。核心技巧在于:

● 二进制倒序生成避免额外反转操作;

● 分组补位与最高位标记简化解码逻辑;

● 字节反转确保大端序输出。

适用于需处理变长二进制数据的场景,算法简洁且易于扩展。未来可探索更紧凑的位运算优化,或结合动态规划优化分组效率。


原创内容 转载请注明出处

分享给朋友:

相关文章

力扣第1991题:寻找数组的中心索引 如何找到左右和相等的中心索引

力扣第1991题:寻找数组的中心索引 如何找到左右和相等的中心索引

题目解读给定一个整数数组,我们需要找到一个中心索引,使得该索引左侧所有元素的和等于右侧所有元素的和。如果不存在这样的索引,则返回-1。中心索引的定义不包含在左右两侧的和计算中。这个问题考察对数组遍历和...

【深度优先搜索实战】力扣547题:省份数量问题的图论解法

【深度优先搜索实战】力扣547题:省份数量问题的图论解法

题目解读‌我们面对的是一个典型的图论问题:给定一个城市的连接矩阵,需要计算其中相互连通的城市群(省份)数量。这个问题可以抽象为无向图中的连通分量计算,每个城市代表图中的一个节点,城市之间的连接关系代表...

力扣5:中心扩散法 轻松破解最长回文子串

力扣5:中心扩散法 轻松破解最长回文子串

题目解读:在一个给定的字符串中,我们需要找到最长的回文子串。回文是指正读反读都相同的字符串,如"aba"、"abba"都是回文。这个问题看似简单,但要在字符串中...

牛客DP41精讲:当背包必须装满时,你的状态转移方程该如何调整?

牛客DP41精讲:当背包必须装满时,你的状态转移方程该如何调整?

题目重解我们面对一个经典背包问题的变体:给定n个物品,每个物品有重量w和价值v,背包容量为V。需要回答两个问题:1) 普通情况下能获得的最大价值;2) 必须恰好装满背包时的最大价值(若无法装满则输出0...

力扣144:递归之美 轻松掌握二叉树前序遍历

力扣144:递归之美 轻松掌握二叉树前序遍历

题目解读二叉树的前序遍历是一种基础但重要的树遍历方式,其遍历顺序为:先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。给定一个二叉树的根节点,我们需要按照这个顺序访问所有节点,并将它们...

2024年GESP四级宝箱题(洛谷P4006)题解:滑动窗口算法优化最长子序列和

2024年GESP四级宝箱题(洛谷P4006)题解:滑动窗口算法优化最长子序列和

一、题目解读本题为2024年GESP四级中的“宝箱”问题(对应洛谷P4006),要求在一个由n个宝箱组成的序列中,找出一个连续子序列,使得该子序列中宝箱价值之和最大,且子序列中最大值与最小值的差不超过...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。