华为OD机试真题--解压原始报文JavaScript

1. 题目

为了提升数据传输的效率,会对传输的报文进行压缩处理。输入一个压缩后的报文,请返回它解压后的原始报文。

  1. 压缩规则:n[str],表示方括号内部的 str 正好重复 n 次。注意 n 为正整数(0 < n <= 100),str只包含小写英文字母,不考虑异常情况。
  2. 输入描述:
    • 输入压缩后的报文:
      1. 不考虑无效的输入,报文没有额外的空格,方括号总是符合格式要求的;
      2. 原始报文不包含数字,所有的数字只表示重复的次数 n ,例如不会出现像 5b 或 3[8] 的输入;
  3. 输出描述:
    • 解压后的原始报文
  4. 注:
    • 原始报文长度不会超过1000,不考虑异常的情况

示例1
输入
3[k]2[mn]
输出
kkkmnmn
说明
k 重复3次,mn 重复2次,最终得到 kkkmnmn

示例2
输入
3[m2[c]]
输出
mccmccmcc
说明
m2[c] 解压缩后为 mcc,重复三次为 mccmccmcc”

2. 解题思路

  1. 对于字符串解析问题,我们很容易想到使用栈来解决。将字符串中除 ] 之外的字符推入栈中,之后当遇到 ] 时,我们让栈中的数字或字母进行出栈,然后进行一些操作。

  2. 那么如何判断什么时候停止出栈呢?毫无疑问,我们需要加一个状态判断,如果满足这个状态,我们就让栈弹出的循环停下来。而这个状态,其实已经存在于字符串中了,那就是 [ 字符。因此我们让 [ 字符也一起入栈,等我们栈顶弹出时发现遇到了第一个 [ 时,就立即停止栈的弹出,然后继续将报文后续的字符入栈,这就是整个解析的大致过程。

  3. 但是问题也随之而来,当我们栈弹出时,我们怎么判断弹出的是一个完整的序列呢?就拿 3[m2[c]] 这串报文来说,我们弹出 2 的时候怎么知道已经解析完了数字部分呢?它也可能是一个两位数或者更多位的数字,因此我们在这里要进行一系列判断,然后还要进行其他操作,比如暂存字符串、数学运算。有没有更好的方法规避这个问题?有的,如果我们提前将报文转成数组,然后让英文字母和数字单独连接在一起,之后我们就不需要进行额外的判断和操作了。具体来说:

    // 对于像下面这种报文
    let str = '34[m22[cn]]';
    // 我们将其转为数组后,将数字和字母分别串联在一起后就变成了下面这样的形式:
    let arr = ['34','[', 'm', '22', '[', 'cn', ']', ']'];
    

    经过这样的转变,我们就无需对数字或者字母的完整性做任何判断,避免了复杂的逻辑。

  4. 解决了栈中字符的完整性判断问题,那么我们就随之而来下一个问题:我们到底是一次弹出一个元素,还是一次弹出两个元素呢? 对于这个问题,其实没有确切的答案,因为可以再栈弹出循环中一次弹出多个字符,不过我的建议是一次弹出两个,最后部分的代码也是以此为基准编写的。

// 解析报文 输入:3[m2[c]] 输出:mccmccmcc
function compiler (order) {
  let stack = []; // 初始化一个栈
  order = order.split('');
  let reg = /[0-9]+/; // 匹配数字
  let reg_letter = /[a-zA-Z]+/; // 匹配字母
  order = merge(order, reg); // 将数字合并,方便栈的使用
  order = merge(order, reg_letter); // // 将字母合并,方便栈的使用
  for (let i = 0; i < order.length; i++) {
    if (order[i] === ']') {
      // 解析报文
      while (stack.length) { // 不断弹出栈顶元素
        let right = stack.pop(); // 由于栈的特性,先弹出的元素位于原报文的右侧
        let left = stack.pop(); // // 由于栈的特性,后弹出的元素位于原报文的左侧
        let flag = false;
        if (left === '[') { // 如果左字符为 [ 字符,说明当前 [] 内的字符已经弹出完毕
          left = stack.pop(); // 由于我们需要获得 [] 内字符的重复次数,因此我们再弹出一个元素,这个元素一定是一个数字
          flag = true; // 这个标志为 true 控制着内循环的退出
        }
        let temp;
        if (reg.test(left)) { // 如果左字符为数字值,则将字符重复相应次数
          temp = right.repeat(+left);
        } else if (reg_letter.test(left)) { // 如果左字符为英文字母,说明左右字符均为字母,则将字符直接拼接
          temp = left + right
        }
        stack.push(temp); // 将处理好的字符串再次加入栈中
        if (flag) break
      }
    } else {
      stack.push(order[i])
    }
  }
  console.log(stack.join(''))
}
// 将报文中的字母和数字分别串联起来
function merge (target, reg) {
  let dealed = []; // 保存处理好的报文
  let temp = [];
  for (let i = 0; i < target.length; i++) {
    if (reg.test(target[i])) { // 如果满足 reg 条件,我们就对其进行拼接
      temp.push(target[i]);
      continue
    } else {
      if (temp.length) { // 将拼接好的字符串加入到结果集中
        dealed.push(temp.join(''));
        temp = [];
      }
      dealed.push(target[i]); // 对于不满足 reg 条件的字符,原样加入到结果集中
    }
  }
  return dealed
}
// compiler('3[m2[c]]')
compiler('3[k]2[mn]')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值