机考E卷100分题 - TLV解码

题目描述

TLV编码是按[Tag Length Value]格式进行编码的,一段码流中的信元用Tag标识,Tag在码流中唯一不重复,Length表示信元Value的长度,Value表示信元的值。

码流以某信元的Tag开头,Tag固定占一个字节,Length固定占两个字节,字节序为小端序。

现给定TLV格式编码的码流,以及需要解码的信元Tag,请输出该信元的Value。

输入码流的16进制字符中,不包括小写字母,且要求输出的16进制字符串中也不要包含小写字母;码流字符串的最大长度不超过50000个字节。

输入描述

输入的第一行为一个字符串,表示待解码信元的Tag;

输入的第二行为一个字符串,表示待解码的16进制码流,字节之间用空格分隔。

输出描述

输出一个字符串,表示待解码信元以16进制表示的Value。

示例1

输入

31
32 01 00 AE 90 02 00 01 02 30 03 00 AB 32 31 31 02 00 32 33 33 01 00 CC

输出

2 33

说明

需要解析的信元的Tag是31,
从码流的起始处开始匹配,
第一个信元的Tag是32,信元长度为1(01 00,小端序表示为1);
第二个信元的Tag是90,其长度为2;
第三个信元的Tag是30,其长度为3;
第四个信元的Tag是31,其长度为2(02 00),
所以返回长度后面的两个字节即可,即32 33。

解题思路

题目要求解析一段以 TLV(Tag, Length, Value)格式编码的码流,找到特定的 Tag,并输出该Tag对应的 Value 部分。具体的TLV格式是这样的:

  1. Tag: 用一个字节表示,是信元的唯一标识符。
  2. Length: 用两个字节表示,表示信元的 Value 部分的长度,采用小端序,即低位字节在前。
  3. Value: 表示信元的值,根据 Length 的值来确定长度。

给定一个16进制的码流和要查找的Tag值,要求找到对应Tag的 Value,并以16进制格式输出。

关键点

  1. 小端序表示的Length

    • 小端序表示法即低字节在前,高字节在后。比如长度为 01 00,表示的实际长度是 1(即 0001)。而 02 00 表示长度 2(即 0002)。
  2. Tag 的固定长度是1字节, Length 固定是2字节,而 Value 的长度是根据 Length 来决定的。

示例详解

输入

31
32 01 00 AE 90 02 00 01 02 30 03 00 AB 32 31 31 02 00 32 33 33 01 00 CC
  • 目标Tag31
  • 码流 是:
    32 01 00 AE 90 02 00 01 02 30 03 00 AB 32 31 31 02 00 32 33 33 01 00 CC
    
    

    解析过程

    1. 第一个信元:

      • Tag: 32
      • Length: 01 00(小端序表示为 1)
      • Value: AE
      • 跳过该信元,因为 Tag 不匹配。
    2. 第二个信元:

      • Tag: 90
      • Length: 02 00(小端序表示为 2)
      • Value: 01 02
      • 跳过该信元,因为 Tag 不匹配。
    3. 第三个信元:

      • Tag: 30
      • Length: 03 00(小端序表示为 3)
      • Value: AB 32 31
      • 跳过该信元,因为 Tag 不匹配。
    4. 第四个信元:

      • Tag: 31
      • Length: 02 00(小端序表示为 2)
      • Value: 32 33
      • Tag 匹配,输出 Value32 33

    因此,输出结果为:

      32 33
    

    Java

      import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            
            // 输入待解码信元的Tag
            String tag = sc.nextLine();
            
            // 输入16进制码流
            String line = sc.nextLine();
            
            // 将16进制码流按空格分割成字符串数组
            String[] hexArray = line.split(" ");
            
            int index = 0;
            while (index < hexArray.length) {
                // 获取当前信元的长度
                int length = Integer.parseInt(hexArray[index + 2] + hexArray[index + 1], 16);
                
                // 如果当前信元的Tag与待解码信元的Tag相同
                if (hexArray[index].equals(tag)) {
                    StringBuilder sb = new StringBuilder();
                    
                    // 将当前信元的Value拼接到StringBuilder中
                    for (int i = index + 3; i < index + 3 + length; i++) {
                        sb.append(hexArray[i]).append(" ");
                    }
                    
                    // 输出待解码信元的Value,转换为大写并去除首尾空格
                    System.out.println(sb.toString().toUpperCase().trim());
                    break;
                } else {
                    // 跳过当前信元
                    index += (2 + length + 1);
                }
            }
        }
    }
    

    Python

      # 定义一个函数,用于将给定的16进制字符串转换为整数
    # length_str 是两个16进制字符拼接后的字符串(表示长度)
    def get_length(length_str):
        return int(length_str, 16)  # 将16进制字符串转换为10进制整数
    
    # 主函数
    def main():
        # 从用户输入中获取目标Tag,并去除首尾空白字符
        tag = input().strip()
    
        # 从用户输入中获取码流数据,并去除首尾空白字符
        line = input().strip()
    
        # 将码流数据按空格分割,存入hex_array数组中,每个元素是一个16进制字符串
        hex_array = line.split(" ")
    
        index = 0  # 初始化索引,用于遍历 hex_array
    
        # 使用while循环遍历整个hex_array
        while index < len(hex_array):
            # 解析当前信元的 Length 字段
            # Length 字段由两个字节组成,先拼接hex_array[index+2](高位)和hex_array[index+1](低位)
            # 再调用get_length函数将其转换为十进制整数
            length = get_length(hex_array[index + 2] + hex_array[index + 1])
    
            # 如果当前信元的Tag与输入的Tag匹配
            if hex_array[index] == tag:
                sb = []  # 定义一个空列表,用于存储匹配Tag后的Value部分
    
                # 从index+3位置开始(即第一个Value字节),提取length个字节的Value
                for i in range(index + 3, index + 3 + length):
                    sb.append(hex_array[i])  # 将每个字节加入sb列表
    
                # 输出提取的Value部分,使用空格连接,并将输出转换为大写,去掉首尾多余空格
                print(" ".join(sb).upper().strip())
                break  # 找到匹配的Tag后,结束循环
            else:
                # 如果当前Tag不匹配,则跳过当前信元
                # 跳过的字节数为:1 个 Tag + 2 个 Length + length 个 Value
                index += (2 + length + 1)
    
    # 检查是否在直接运行脚本
    if __name__ == "__main__":
        main()  # 调用主函数
    

    JavaScript

    // 引入 'readline' 模块,用于读取标准输入输出
    const readline = require('readline');
    
    // 创建接口实例,用于处理输入输出
    const rl = readline.createInterface({
      input: process.stdin,   // 标准输入流
      output: process.stdout  // 标准输出流
    });
    
    // 首次监听 'line' 事件,获取用户输入的目标 Tag
    rl.on('line', (tag) => {
    
      // 第二次监听 'line' 事件,获取码流数据
      rl.on('line', (line) => {
        // 将码流按空格分割,存入 hexArray 数组
        const hexArray = line.split(" ");
        
        let index = 0;  // 定义索引,用于遍历 hexArray
    
        // 使用循环遍历整个 hexArray
        while (index < hexArray.length) {
          // 解析 Length 字段。Length 由两个字节组成,先拼接后转为16进制整数
          const length = parseInt(hexArray[index + 2] + hexArray[index + 1], 16);
    
          // 如果当前的 Tag 和用户输入的 Tag 匹配
          if (hexArray[index] === tag) {
            const sb = [];  // 定义一个数组,用于存储 Value 部分
            
            // 将 Tag 后的 length 个字节的 Value 加入 sb 数组
            for (let i = index + 3; i < index + 3 + length; i++) {
              sb.push(hexArray[i]);
            }
    
            // 将 sb 数组中的 Value 转换为大写字符串并输出,去掉多余空格
            console.log(sb.join(" ").toUpperCase().trim());
            break;  // 找到目标 Tag 后,结束循环
          } else {
            // 如果 Tag 不匹配,跳过当前信元
            // 跳过的字节数为:1 个 Tag + 2 个 Length + length 个 Value
            index += (2 + length + 1);
          }
        }
    
        // 关闭 readline 接口,结束程序
        rl.close();
      });
    });
    

    C++

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        string tag;  // 定义一个字符串变量存储要查找的Tag
        cin >> tag;  // 从输入中读取用户提供的Tag值
        
        string tmp;  // 临时字符串变量,用于存储每次输入的16进制字节
        vector<string> vec;  // 定义一个字符串向量,用于存储码流中按空格分隔的所有16进制字符串
    
        // 使用循环从标准输入中读取码流数据,直到遇到换行符为止
        while (cin >> tmp) {
            vec.push_back(tmp);  // 将每个16进制字节字符串存入向量
            if (cin.get() == '\n') break;  // 如果读取到换行符,停止输入
        }
    
        // 开始遍历向量vec,解析码流数据
        for (int i = 0; i < vec.size();) {
            string t = vec[i];  // 读取当前信元的Tag,即vec[i]
    
            // 解析Length字段(两个字节,小端序)
            // vec[i+1] 是低位字节,vec[i+2] 是高位字节
            // 计算方式为:低位 + 高位 * 16
            int len = stoi(vec[i + 1]) + 16 * stoi(vec[i + 2]);
    
            // 如果当前信元的Tag与目标Tag一致
            if (t == tag) {
                // 输出该Tag对应的Value部分
                // 从i + 3(第一个Value字节)开始,输出len个字节的Value
                for (int j = i + 3; j < i + 3 + len; j++) {
                    cout << vec[j] << " ";  // 输出Value的每个字节,用空格分隔
                }
                cout << endl;  // 输出完毕后换行
                break;  // 找到匹配的Tag后,直接退出循环
            } else {
                // 如果当前Tag不匹配,则跳过该信元
                // 跳过Tag(1字节)+ Length(2字节)+ Value(len字节)
                i = i + 2 + len + 1;  // 移动i的位置,跳到下一个信元
            }
        }
    
        return 0;  // 程序结束
    }
    

    C

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_LEN 50000  // 定义码流的最大长度
    
    int main() {
        char tag[3];  // 存储要查找的信元Tag,2个字符+1个结尾字符
        scanf("%s", tag);  // 输入要查找的Tag值
    
        char stream[MAX_LEN];  // 存储整条码流的输入
        getchar();  // 吸收掉换行符
    
        fgets(stream, MAX_LEN, stdin);  // 读取整条码流字符串,空格分隔
    
        // 定义一个数组来存储分割后的16进制字符串
        char *vec[MAX_LEN];
        int vec_size = 0;
    
        // 使用 strtok 函数将码流按空格分割
        char *tmp = strtok(stream, " ");
        while (tmp != NULL) {
            vec[vec_size++] = tmp;  // 将分割出的部分存入数组
            tmp = strtok(NULL, " ");
        }
    
        // 遍历解析码流
        for (int i = 0; i < vec_size;) {
            char *t = vec[i];  // 当前的Tag
            // 将长度部分解析为整数(小端序处理),vec[i+1]是低位,vec[i+2]是高位
            int len = atoi(vec[i + 1]) + 16 * atoi(vec[i + 2]);
    
            // 如果当前Tag与目标Tag相同
            if (strcmp(t, tag) == 0) {
                // 输出该信元的Value部分
                for (int j = i + 3; j < i + 3 + len; j++) {
                    printf("%s ", vec[j]);
                }
                printf("\n");
                break;  // 找到后直接跳出循环
            } else {
                // 如果Tag不匹配,跳过当前信元,i += 2 (长度字节) + len (Value长度) + 1 (Tag字节)
                i = i + 2 + len + 1;
            }
        }
    
        return 0;
    }
    
#华为##OD##牛客创作赏金赛#

主要记录自己的刷题日常,学而时习之。

全部评论
这道又是看不懂
点赞 回复 分享
发布于 11-13 17:55 广东

相关推荐

刚刚做了华为Java机考,人是懵的。三道题两小时,每道题都看着不难都有思路结果写完过了测试一提交只有10%通过率,最后只有第一题提到了85%,最后算下来135分过不了150线,我愧对期待值拉满的HR,愧对我自己的复习。。。还是没刷够和基础不足,但至少把题目发这里大伙帮我解决一下这个遗憾吧。(不是,哥们。发现十拿九稳的主思路只能过10%,真得懵吧)第一题100:游客参观总时长问游乐园每天开放多久能招待所有游客。游乐园每一段时间能接受任意个数游客。每一行给一个游客的参观时长[1,5],[1,2],&nbsp;[10,11],输出总时长&nbsp;4+1=5.&nbsp;数值全在10^6以下。(确认输入无误)我一开始想做合并时间段最后算累加,结果Arraylist写下来边界判断什么的瞎闹连测试案例都过不了。三道题都写完之后回来检查,改了方法,求出参观时长的最大值,以它为长度建立空数组,再遍历游客时间插入1.最后数1,数到0就断掉算长度累加。通过率到了85%,但是给的反馈是测试用例运行错误,不是超时,我就只能继续检查下一题去了。(没想到后续检查没救得了我)第二题200:字符串集合求交集(这个是我最懵的,教教)(不需要检查输入)题目意思非常简单,给你输入几个字符串集合,{3(长度)&nbsp;123&nbsp;456&nbsp;789}第一个集合.{&nbsp;2&nbsp;456&nbsp;789}第二个集合。输出每个集合交集最大的集合和长度&nbsp;2&nbsp;2&nbsp;\n&nbsp;1&nbsp;2&nbsp;。字符串完全相等就是交集的元素。就这么简单。&nbsp;我Hashmap存集合的输入顺序,也就是集合的序号,value存的是字符串ArrayList。然后按总输出的值遍历这个输入顺序下标获取那个字符串集合,然后和另一个下标对应的字符串集合遍历。四层遍历求个相等的情况+1,记录最长值和最长集合序号。过了测试例以后提交,10%,还是答案错误不是超时,我人懵了,回来检查的时候也还是懵的。各路大神务必教教我这必须查相等的遍历为什么过了基础例子然后只能过10%的测试。有什么优化办法能既考虑边界值也能简化时长的?第三题300:摘水果也蛮直白的,给你个正方形地图,然后每个格子上是水果的数量,如果不能走就是-1.&nbsp;果农要从左上角走到右下角,只能向下或向右。走到右下角以后他再从右下角折回来走(没限制怎么走回来)到左上角,问你他能采摘的最大水果数量。这题我承认肯定是算法没想明白,漏了什么很关键的东西。(应该早点放弃检查的,很烦)这题我一开始想搞个递归往下找记录总数改变地图数字,然后往上找再递归找路最后加起来。但是写出来了向下找路然后发现这个找路和求最大值的路想做复原太诡异了,(现在想想完全可以找到路了记下来再找到最大值,很有可能能避免一些case)就放弃了递归投向动态规划。我累加了一次dp所有值抵达右下角以后,发现这个格子里的值正好是走下去走上来能拿到的最大值,想了一下应该也对,如果从左上角走不下来,那也不可能从那条路折回来,所有通路值加到最后应该就是答案。(难道说!是多个通路,只能取2条最大?!状态转移没这么简单才对!)状态转移方程就是等于非-1的左侧和上侧的格子的值相加再加上本来格子里的值。如果左上都是-1那我直接设为0.(难道该设为-1?复盘才发现槽点太多)(给地图加了一行一列全设为0,从1开始遍历到n。)往回走也不可能走这里。最后得分10%。求解!
牛客141057821号:我用python做的 第一道题记得leetcode有原题,先用开始参观时间sort一下然后指针遍历求set union 第二道题我python暴力解法全过。。 第三道题我是两遍dp,已从从左上到右下一次右下到左上,中间把第一次遍历走过的格子设成0就完了。 话说150是分数线吗?可以问下哪里的消息么
查看3道真题和解析 投递华为等公司10个岗位
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务