题目描述
已知火星人使用的运算符为#、$,其与地球人的等价公式如下:
x#y = 2x+3y+4
x$y = 3*x+y+2
其中x、y是无符号整数
地球人公式按C语言规则计算
火星人公式中,$的优先级高于#,相同的运算符,按从左到右的顺序计算
现有一段火星人的字符串报文,请你来翻译并计算结果。
输入描述
火星人字符串表达式(结尾不带回车换行)
输入的字符串说明:字符串为仅由无符号整数和操作符(#、$)组成的计算表达式。
例如:123#4$5#67$78。
用例保证字符串中,操作数与操作符之间没有任何分隔符。
用例保证操作数取值范围为32位无符号整数。
保证输入以及计算结果不会出现整型溢出。
保证输入的字符串为合法的求值报文,例如:123#4$5#67$78
保证不会出现非法的求值报文,例如类似这样字符串:
#4$5 //缺少操作数
4$5# //缺少操作数
4#$5 //缺少操作数
4 $5 //有空格
3+4-5*6/7 //有其它操作符
12345678987654321$54321 //32位整数计算溢出
输出描述
根据输入的火星人字符串输出计算结果(结尾不带回车换行)。
用例
输入 7#6$5#12
输出 226
说明
7#6$5#12
=7#(3*6+5+2)#12
=7#25#12
=(27+325+4)#12
=93#12
=293+312+4
=226
思路
1. 双栈法(数组模拟栈)
- 核心步骤:
- 拆分表达式为数字数组(nums)和运算符数组(ops);
- 高优先级优先:遍历ops,遇
$则取出nums中对应两个数计算(3x+y+2),替换nums并删除该$,重复至无$; - 低优先级处理:遍历剩余ops,遇
#则取出nums中对应两个数计算(2x+3y+4),替换nums并删除该#; - 最终nums中唯一元素即为结果。
- 核心逻辑:通过数组模拟栈结构,按优先级分批次修改数组,逐步合并计算结果。
- 时空复杂度:时间复杂度O(n)(n为表达式长度),空间复杂度O(n)。
2. 正则表达式替换法
- 核心步骤:
- 高优先级处理:用正则匹配
数字$数字格式子串,计算结果后替换原串,循环至无$; - 低优先级处理:将剩余字符串按
#分割为数字数组,从左到右依次计算#运算; - 输出最终累加结果。
- 高优先级处理:用正则匹配
- 核心逻辑:利用正则精准匹配高优先级运算子串,替换后简化表达式,再处理低优先级运算。
- 时空复杂度:时间复杂度O(n²)(最坏情况)/ O(n)(最好情况),空间复杂度O(n)(n为表达式长度)。
3. 调度场算法(线性扫描+分步求值)
- 核心步骤:
- 分词:将表达式拆分为数字、运算符的token列表;
- 高优先级求值:遍历token,遇
$则弹出前一个数字与后一个数字计算,结果入临时数组,跳过已计算的数字; - 低优先级求值:遍历临时数组,从左到右依次计算所有
#运算; - 输出最终结果。
- 核心逻辑:线性扫描token流,优先处理高优先级运算符,将结果融入临时数组,再顺序处理低优先级运算符。
- 时空复杂度:时间复杂度O(n)(n为表达式长度),空间复杂度O(n)。
代码
// 双栈法
function solution() {
const s = readline();
// 步骤1:解析字符串为数字数组和运算符数组
const nums = [];
const ops = [];
let numStr = '';
for (const c of s) {
if (c === '#' || c === '$') {
// 遇到运算符,将之前的数字入栈
nums.push(Number(numStr));
numStr = '';
ops.push(c);
} else {
// 拼接数字字符串
numStr += c;
}
}
// 最后一个数字入栈
nums.push(Number(numStr));
// 步骤2:先处理所有$运算符(优先级高)
let i = 0;
while (i < ops.length) {
if (ops[i] === '$') {
// 计算x$y = 3x + y + 2
const x = nums[i];
const y = nums[i + 1];
nums.splice(i, 2, 3 * x + y + 2); // 替换为结果
ops.splice(i, 1); // 移除当前$
// 索引不变,继续检查当前位置(可能有连续$)
} else {
i++; // 不是$,继续下一个
}
}
// 步骤3:处理剩余的#运算符(优先级低)
i = 0;
while (i < ops.length) {
if (ops[i] === '#') {
// 计算x#y = 2x + 3y + 4
const x = nums[i];
const y = nums[i + 1];
nums.splice(i, 2, 2 * x + 3 * y + 4); // 替换为结果
ops.splice(i, 1); // 移除当前#
// 索引不变,继续检查当前位置
} else {
i++;
}
}
// 最终结果为nums中唯一的元素
console.log(nums[0]);
}
// 正则表达式
function solution2() {
let s = readline();
// 匹配形如"数字$数字"的正则表达式
const pattern = /(\d+)\$(\d+)/;
// 循环替换所有符合规则的子串,直到没有匹配
while (pattern.test(s)) {
const match = pattern.exec(s);
const x = parseInt(match[1], 10);
const y = parseInt(match[2], 10);
// 替换第一个匹配项为3x + y + 2的结果
s = s.replace(pattern, 3 * x + y + 2);
}
// 按"#"分割并转换为数字数组
const arr = s.split('#').map(Number);
// 计算最终结果
let x = arr[0];
for (const y of arr.slice(1)) {
x = 2 * x + 3 * y + 4;
}
console.log(x);
}
// 调度场算法
function solution3() {
const s = readline().trim();
// Step 1: Tokenize
const tokens = [];
let num = '';
for (let c of s) {
if (c === '#' || c === '$') {
tokens.push(Number(num));
tokens.push(c);
num = '';
} else {
num += c;
}
}
tokens.push(Number(num)); // last number
// Step 2: Evaluate all $ first (higher precedence)
const afterDollar = [];
for (let i = 0; i < tokens.length; i++) {
if (tokens[i] === '$') {
const x = afterDollar.pop();
const y = tokens[i + 1];
const res = 3 * x + y + 2;
afterDollar.push(res);
i++; // skip next number
} else {
afterDollar.push(tokens[i]);
}
}
// Step 3: Evaluate all # from left to right
let result = afterDollar[0];
for (let i = 1; i < afterDollar.length; i += 2) {
const op = afterDollar[i];
const y = afterDollar[i + 1];
if (op === '#') {
result = 2 * result + 3 * y + 4;
}
}
console.log(result);
}
// 测试用例
const cases = [
`7#6$5#12`, // 输出226
`123#4$5#67$78`, // 正确计算:先处理4$5=3*4+5+2=19,67$78=3*67+78+2=201+78+2=281;再处理123#19=2*123+3*19+4=246+57+4=307;最后307#281=2*307+3*281+4=614+843+4=1461
`1$2$3`, // 1$2=3*1+2+2=7,7$3=3*7+3+2=26 → 输出26
`3#4$5#6` // 4$5=3*4+5+2=19;3#19=2*3+3*19+4=6+57+4=67;67#6=2*67+3*6+4=134+18+4=156 → 输出156
];
let caseIndex = 0, lineIndex = 0;
const readline = (() => {
let lines = [];
return () => {
if (!lineIndex) lines = cases[caseIndex].trim().split('\n').map(l => l.trim());
return lines[lineIndex++];
};
})();
cases.forEach((_, i) => {
caseIndex = i;
lineIndex = 0;
solution3();
console.log('-------');
});
1060

被折叠的 条评论
为什么被折叠?



