信号解码

95 阅读3分钟

信号解码

问题背景

无线基站会接收到手机上报的特定格式的加密信息,例如 11@2$3@14。在处理这些信息之前,需要根据一套固定的规则对其进行解码和计算。

解码规则

信息由非负整数和两种特殊运算符 (@, $) 构成。解码规则如下:

  1. 运算符定义

    • 对于 x@y,其计算公式为:x@y=2x+y+3
    • 对于 x$y,其计算公式为:x$y=3x+2y+1
    • 其中 xy 均为非负整数。
  2. 运算优先级

    • 运算符 @ 的优先级 高于 运算符 $
    • 在计算整个表达式时,需要先完成所有 @ 运算,再进行 $ 运算。
  3. 计算顺序

    • 对于 相同 的运算符,计算顺序为 从左到右

输入格式

  • message: 一个字符串,代表待解码的加密信息。

    • 输入字符串仅包含数字字符 (0-9) 和特殊运算符 (@, $)。

    • 字符串长度 message.length 满足 3 <= message.length <= 100

    • 用例保证

      • 输入合法,运算符 (@, $) 的左右两侧必定是数字。
      • 数字没有前导零(例如,不会出现 07)。
      • 不会出现 4@$5 这样运算符相邻的格式。
      • 输入的数字、计算过程中的中间值以及最终结果,其取值范围都在 [0, 2^31 - 1] 之内。

输出格式

  • 一个整数,表示解码和计算后的最终结果。

样例说明

样例 1

  • 输入: "11@2$3@14"

  • 输出: 128

  • 计算过程分解:

    1. 分析: 表达式中包含 @$ 两种运算符。根据优先级规则,首先从左到右计算所有 @ 运算。

    2. 计算第一个@ : 11@2

      • 套用公式 x@y=2x+y+3
      • 2×11+2+3=22+2+3=27
      • 原表达式变为: 27$3@14
    3. 计算第二个@ : 3@14

      • 套用公式 x@y=2x+y+3
      • 2×3+14+3=6+14+3=23
      • 原表达式变为: 27$23
    4. 计算$ : 27$23

      • 所有 @ 运算已完成,现在计算 $
      • 套用公式 x$y=3x+2y+1
      • 3×27+2×23+1=81+46+1=128
    5. 最终结果: 128

样例 2

  • 输入: "103$104@105@2$106@107"

  • 输出: 5397

  • 计算过程分解:

    1. 分析: 同样,先计算所有 @ 运算。表达式中有三个 @ 运算:104@105... @2 (前一步的结果),和 106@107

    2. 计算 104@105@2:

      • 这是一个连续的 @ 运算,按从左到右的顺序进行。

      • 第一步: 104@105

        • 2×104+105+3=208+105+3=316
      • 第二步: 316@2 (用上一步的结果 316 作为 x)

        • 2×316+2+3=632+2+3=637
    3. 计算 106@107:

      • 2×106+107+3=212+107+3=322
    4. 替换原表达式: 将所有 @ 的计算结果代回,原表达式变为 103$637$322

    5. 计算 103$637$322:

      • 这是一个连续的 $ 运算,按从左到右的顺序进行。

      • 第一步: 103$637

        • 3×103+2×637+1=309+1274+1=1584
      • 第二步: 1584$322 (用上一步的结果 1584 作为 x)

        • 3×1584+2×322+1=4752+644+1=5397
    6. 最终结果: 5397

/**
 * 解决“信号解码”问题的实现类.
 * 核心思想是根据运算符优先级,分两步进行计算。
 */
public class SignalDecoder {

    /**
     * 主方法,对给定的消息字符串进行解码和计算.
     * @param message 包含数字和特殊运算符(@, $)的字符串
     * @return 最终的计算结果
     */
    public int decode(String message) {
        // --- 步骤 1: 处理高优先级的 '@' 运算 ---

        // 使用 '$' 作为分隔符,将原始表达式分割成多个子表达式。
        // 每个子表达式要么是一个纯数字,要么是一个仅包含 '@' 运算符的表达式。
        // 注意:String.split() 的参数是正则表达式,'$' 是特殊字符,表示结束位置,需要用 "\$" 来转义。
        String[] atExpressions = message.split("\$");

        // 创建一个数组来存储处理完 '@' 运算后的中间结果。
        // 这些结果将成为 '$' 运算的操作数。
        long[] dollarOperands = new long[atExpressions.length];
        for (int i = 0; i < atExpressions.length; i++) {
            // 对每个子表达式调用辅助方法进行计算
            dollarOperands[i] = calculateAtExpression(atExpressions[i]);
        }

        // --- 步骤 2: 处理低优先级的 '$' 运算 ---

        // 如果只有一个操作数(即原始表达式中没有'$'),那么它就是最终结果。
        if (dollarOperands.length == 1) {
            return (int) dollarOperands[0];
        }

        // 以第一个操作数作为初始结果
        long finalResult = dollarOperands[0];
        
        // 从左到右,依次将后续的操作数通过 '$' 运算累积到结果中
        for (int i = 1; i < dollarOperands.length; i++) {
            long nextOperand = dollarOperands[i];
            // 应用公式: x$y = 3*x + 2*y + 1
            finalResult = 3 * finalResult + 2 * nextOperand + 1;
        }

        // 将 long 类型的最终结果转换为 int 并返回
        return (int) finalResult;
    }

    /**
     * 辅助方法:计算一个只包含 '@' 运算符的子表达式的值.
     * @param expr 一个子表达式,例如 "104@105@2" 或 "103"
     * @return 该子表达式的计算结果
     */
    private long calculateAtExpression(String expr) {
        // 使用 '@' 作为分隔符,将子表达式分割成纯数字字符串
        String[] numbersStr = expr.split("@");

        // 将第一个数字作为计算的初始值
        // 使用 long 类型进行计算,以防止中间过程溢出,这是一种良好的编程习惯。
        long result = Long.parseLong(numbersStr[0]);

        // 如果只有一个数字(即子表达式中没有'@'),直接返回该数字
        if (numbersStr.length == 1) {
            return result;
        }

        // 从左到右,依次将后续的数字通过 '@' 运算累积到结果中
        for (int i = 1; i < numbersStr.length; i++) {
            long nextOperand = Long.parseLong(numbersStr[i]);
            // 应用公式: x@y = 2*x + y + 3
            result = 2 * result + nextOperand + 3;
        }

        return result;
    }
}