【算法】TLV格式解析实例:华为OD机考双机位A卷 - TLV解析 Ⅱ

题目描述

两端通过TLV格式的报文通信,现在收到对端一个TLV格式的消息包,需要生成匹配后的`(tag, length, valueOffset)`列表,具体要求如下:

  1. 消息包中多组`tag、length、value`紧密排列,其中`tag、length`各占1字节(uint8类型),`value`所占字节数等于`length`的值。

  2. 结果数组中`tag`值已知,需填充每个`tag`对应数据的`length`和`valueOffset`值(`valueOffset`是`value`在原消息包中的起始偏移量,从0开始,以字节为单位);需将消息包中的`tag`与结果数组中的`tag`匹配(可能匹配失败:若结果数组的`tag`在消息包中不存在,则`length`和`valueOffset`都为0)。

  3. 消息包和结果数组中的`tag`值都按升序排列,且不重复。

  4. 消息包未被篡改,但尾部可能不完整,不完整的一组TLV需丢弃。

输入描述

第一行:一个字符串,代表收到的消息包,字符串长度在10000以内。

  • 说明1:字符串使用十六进制文本格式(字母为大写)展示数据,例如`0F04ABABABAB`代表一组TLV:前两个字符`0F`代表`tag`值为15,接下来两个字符`04`代表`length`值为4字节,再接下来8个字符是4字节的`value`。

  • 说明2:输入字符串中,每组TLV紧密排列,无空格等分隔符。

第二行:需要匹配的`tag`数量`n`(0 < n < 1000)。

后面`n`行:需要匹配的`n`个`tag`值(十进制表示),按递增排列。

输出描述

输出与需要匹配的`n`个`tag`对应的`n`行匹配结果,每行由`length`和偏移量组成。


算法一

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>

using namespace std;

/**
 * @brief 消息信息结构体
 * 存储每个消息字段的解析信息
 */
struct MsgInfo {
    int msgLenInt;      // 消息值的长度(字节数)
    int valueOffset;    // 消息值在原始消息中的偏移量(字节位置)
    bool isUsed;        // 标记该字段是否被查询过(当前代码中未充分利用)
};

/**
 * @brief 将两位十六进制字符串转换为整数
 * @param str 两位十六进制字符串(如"1F")
 * @return 对应的十进制整数值
 * 
 * 注意:此函数只处理大写字母A-F,且没有错误检查
 * 实际工程中应该处理小写字母和非法字符
 */
int strToInt(const string& str) {
    int size = str.size();                    // 获取字符串长度(应为2)
    vector<int> res(size, 0);                 // 存储每个字符转换后的数字
    
    for (int i = 0; i < size; i++) {
        if (str[i] >= '0' && str[i] <= '9') {
            res[i] = str[i] - '0';            // 数字字符转换
        } else if (str[i] >= 'A' && str[i] <= 'F') {
            res[i] = str[i] - 'A' + 10;       // 大写字母A-F转换
        }
        // 注意:没有处理小写字母a-f,也没有处理非法字符
    }
    
    // 将两个十六进制位组合成一个字节:高位*16 + 低位
    return res[0] * 16 + res[1];
}

int main() {
    // -------------------- 输入部分 --------------------
    string message;      // 原始消息字符串(十六进制表示)
    getline(cin, message);  // 读取整行作为消息
    
    int tagNum;          // 要查询的标签数量
    cin >> tagNum;
    
    vector<int> tags(tagNum);  // 存储要查询的标签数组
    for (int i = 0; i < tagNum; i++) {
        cin >> tags[i];        // 逐个读取查询标签
    }
    
    // -------------------- 解析消息并构建映射表 --------------------
    // 使用哈希表存储标签到消息信息的映射,实现O(1)查找
    unordered_map<int, MsgInfo> msgInfoMap;
    
    int len = message.size();  // 消息字符串总长度
    
    // 遍历消息字符串,解析每个字段
    // 格式:2字符标签 + 2字符长度 + N*2字符值(N为长度值)
    for (int i = 0; i < len - 3; ) {  // 至少需要4个字符才能构成一个完整字段
        // 1. 提取并转换标签(前2个字符)
        int tagInt = strToInt(message.substr(i, 2));
        
        // 2. 提取并转换长度(接下来的2个字符)
        int msgLenInt = strToInt(message.substr(i + 2, 2));
        
        // 3. 计算值在原始消息中的偏移量(字节位置)
        // (i+5)/2 是因为:i是字符索引,每2个字符对应1个字节
        // +5是因为:跳过标签(2)和长度(2)共4个字符,再+1得到字节位置
        int msgValueOffset = (i + 4) / 2;
        
        // 4. 将解析结果存入哈希表,标签作为key
        // 注意:如果有重复标签,这里会覆盖之前的值
        msgInfoMap[tagInt] = {msgLenInt, msgValueOffset, false};
        
        // 5. 跳过当前字段:标签(2) + 长度(2) + 值(msgLenInt*2)
        i += 4 + msgLenInt * 2;  // 移动到下一个字段的起始位置
        
        // 6. 检查是否越界,防止解析不完整的字段
        if (i >= len) {
            break;
        }
    }
    
    // -------------------- 查询并输出结果 --------------------
    for (int i = 0; i < tagNum; i++) {
        // 在哈希表中查找标签
        auto it = msgInfoMap.find(tags[i]);
        
        if (it != msgInfoMap.end()) {
            // 找到标签:输出长度和偏移量
            it->second.isUsed = true;  // 标记为已使用(当前逻辑未使用此标记)
            cout << it->second.msgLenInt << " " << it->second.valueOffset << endl;
        } else {
            // 未找到标签:输出"0 0"
            cout << "0 0" << endl;
        }
    }
    
    return 0;
}

算法流程总结

  1. 读取输入的十六进制消息字符串和查询标签列表

  2. 解析消息字符串,提取每个字段的标签、长度和值

  3. 将标签作为key,消息信息作为value存入哈希表

  4. 对每个查询标签,在哈希表中查找并输出对应信息

时间复杂度分析

消息解析:O(L),L为消息长度

查询:O(N),N为查询数量

总复杂度:O(L + N)

空间复杂度分析

哈希表:O(M),M为消息中字段数量

其他:O(L + N)

潜在问题

  1. strToInt函数没有处理小写字母a-f

  2. 没有验证十六进制字符的有效性

  3. 如果消息格式错误(如长度字段为奇数),可能产生错误结果

  4. 重复标签会被覆盖,只保留最后一个

算法二

cpp 复制代码
#include <iostream>
#include <string>
#include <cctype>

#define MAX_MSG_LENGTH 1000
#define MAX_TAGS 100
#define MAX_MAP_SIZE 200

// 定义Map中存储的值结构
struct TagInfo {
    int tag;
    int length;
    int valueOffset;
    bool isUsed;  // 用于标记此项是否有效
};

// 将16进制字符串转换为整数
int hexToInt(const std::string& hexStr) {
    int result = 0;
    for (char c : hexStr) {
        int value;
        if (c >= '0' && c <= '9') {
            value = c - '0';
        } else {
            value = tolower(c) - 'a' + 10;
        }
        result = result * 16 + value;
    }
    return result;
}

int main() {
    std::string msg;
    int n;
    int tags[MAX_TAGS];

    // 读取输入
    std::cin >> msg;
    std::cin >> n;

    for (int i = 0; i < n; i++) {
        std::cin >> tags[i];
    }

    // 创建map结构,使用数组实现
    TagInfo tagMap[MAX_MAP_SIZE];
    // 初始化所有项为未使用
    for (int i = 0; i < MAX_MAP_SIZE; i++) {
        tagMap[i].isUsed = false;
    }

    int msgLen = msg.length();

    // 解析msg,填充哈希表
    for (int i = 0; i + 3 < msgLen; i++) {
        int tag, len, valueOffset;
        std::string tagStr = msg.substr(i, 2);
        std::string lenStr = msg.substr(i + 2, 2);

        // 将16进制字符串转换为整数
        tag = hexToInt(tagStr);
        len = hexToInt(lenStr);

        // 计算valueOffset
        valueOffset = (i + 5) / 2;

        // 跳过value,更新i
        i += 3 + len * 2;

        // 如果i超出msg长度,跳出循环
        if (i >= msgLen) break;

        // 将tag、length、valueOffset存入哈希表中
        for (int j = 0; j < MAX_MAP_SIZE; j++) {
            if (!tagMap[j].isUsed) {
                tagMap[j].tag = tag;
                tagMap[j].length = len;
                tagMap[j].valueOffset = valueOffset;
                tagMap[j].isUsed = true;
                break;
            }
        }
    }

    // 遍历tags,输出匹配结果
    for (int i = 0; i < n; i++) {
        bool found = false;
        for (int j = 0; j < MAX_MAP_SIZE; j++) {
            if (tagMap[j].isUsed && tagMap[j].tag == tags[i]) {
                std::cout << tagMap[j].length << " " << tagMap[j].valueOffset << std::endl;
                found = true;
                break;
            }
        }

        if (!found) {
            std::cout << "0 0" << std::endl;
        }
    }

    return 0;
}

算法对比分析

算法一(基于unordered_map)在通用性、正确性和效率上全面优于算法二(基于静态数组),更适合解决这个题目。

对比维度 算法一(使用 unordered_map 算法二(使用静态数组) 优劣分析
核心数据结构 哈希表 (unordered_map) 固定大小的结构体数组 算法一优。哈希表查找效率高;数组需要遍历,效率低。
时间复杂度 解析O(L),查询O(N),整体O(L+N) 解析O(L×M),查询O(N×M),整体O((L+N)×M) 算法一优。哈希表插入和查询平均是常数时间,远快于数组的线性查找。
空间复杂度 O(M),M为实际有效的TLV字段数 O(MAX_MAP_SIZE),固定为200 算法一优。按需分配,不浪费;数组可能浪费空间或不够用。
正确性/健壮性 高。能自动处理数据边界,适配题目数据规模。 。存在硬伤:1. 固定容量(200)易溢出;2. valueOffset计算公式错误。 算法一优。算法二有严重的设计缺陷,无法保证正确性。
代码可读性 高。逻辑清晰,利用标准库,易于理解和维护。 较低。需手动管理数组和状态,逻辑较复杂。 算法一优
适用场景 通用场景,尤其适合数据量不确定或较大的情况。 仅适用于数据量极小(远小于200)且内存极度受限的特定环境。 算法一更通用
相关推荐
OC溥哥9998 小时前
Paper MinecraftV3.0重大更新(下界更新)我的世界C++2D版本隆重推出,拷贝即玩!
java·c++·算法
Jayden_Ruan8 小时前
C++蛇形方阵
开发语言·c++·算法
星火开发设计8 小时前
C++ map 全面解析与实战指南
java·数据结构·c++·学习·算法·map·知识
执笔论英雄8 小时前
【RL] advantages白化与 GRPO中 advantages均值,怎么变化,
算法·均值算法
2301_800895108 小时前
hh的蓝桥杯每日一题
算法·职场和发展·蓝桥杯
老鱼说AI8 小时前
现代计算机系统1.2:程序的生命周期从 C/C++ 到 Rust
c语言·c++·算法
仰泳的熊猫8 小时前
题目1099:校门外的树
数据结构·c++·算法·蓝桥杯
求梦8209 小时前
【力扣hot100题】反转链表(18)
算法·leetcode·职场和发展
NAGNIP9 小时前
机器学习特征工程中的特征选择
算法·面试