题目描述
两端通过TLV格式的报文通信,现在收到对端一个TLV格式的消息包,需要生成匹配后的`(tag, length, valueOffset)`列表,具体要求如下:
-
消息包中多组`tag、length、value`紧密排列,其中`tag、length`各占1字节(uint8类型),`value`所占字节数等于`length`的值。
-
结果数组中`tag`值已知,需填充每个`tag`对应数据的`length`和`valueOffset`值(`valueOffset`是`value`在原消息包中的起始偏移量,从0开始,以字节为单位);需将消息包中的`tag`与结果数组中的`tag`匹配(可能匹配失败:若结果数组的`tag`在消息包中不存在,则`length`和`valueOffset`都为0)。
-
消息包和结果数组中的`tag`值都按升序排列,且不重复。
-
消息包未被篡改,但尾部可能不完整,不完整的一组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;
}
算法流程总结
-
读取输入的十六进制消息字符串和查询标签列表
-
解析消息字符串,提取每个字段的标签、长度和值
-
将标签作为key,消息信息作为value存入哈希表
-
对每个查询标签,在哈希表中查找并输出对应信息
时间复杂度分析
消息解析:O(L),L为消息长度
查询:O(N),N为查询数量
总复杂度:O(L + N)
空间复杂度分析
哈希表:O(M),M为消息中字段数量
其他:O(L + N)
潜在问题
-
strToInt函数没有处理小写字母a-f
-
没有验证十六进制字符的有效性
-
如果消息格式错误(如长度字段为奇数),可能产生错误结果
-
重复标签会被覆盖,只保留最后一个
算法二
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)且内存极度受限的特定环境。 | 算法一更通用。 |