[中等] 字符串解码 - Leecode 394
题目
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例
示例 1:
输入: s = "3[a]2[bc]"
输出: "aaabcbc"
示例 2:
输入: s = "3[a2[c]]"
输出: "accaccacc"
示例 3:
输入: s = "2[abc]3[cd]ef"
输出: "abcabccdcdcdef"
示例 4:
输入: s = "abc3[cd]xyz"
输出: "abccdcdcdxyz"
提示:
1 <= s.length <= 30s由小写英文字母、数字和方括号'[]'组成s保证是一个 有效 的输入。s中所有整数的取值范围为[1, 300]
解法
var decodeString = function(s) {
const stack = [];
let num = ''; // 用于存储数字
for (let i = 0; i < s.length; i++) {
const char = s[i];
if (/\d/.test(char)) {
// 如果是数字,就继续累加到 num 中
num += char;
} else if (char === '[') {
// 如果是 '[', 把当前的数字压入栈中
if (num) {
stack.push(Number(num));
num = ''; // 重置 num,准备下一个数字
}
// 压入 '['
stack.push(char);
} else if (char === ']') {
// 如果是 ']', 处理解码
let temp = '';
// 弹出栈中的元素,直到遇到 '['
while (stack[stack.length - 1] !== '[') {
temp = stack.pop() + temp;
}
stack.pop(); // 弹出 '['
let count = stack.pop(); // 获取重复次数
// 将重复的子串压入栈中
stack.push(temp.repeat(count));
} else {
// 如果是字母,直接压入栈中
stack.push(char);
}
}
// 最后将栈中的内容连接成最终的字符串返回
return stack.join('');
};
[中等] 基本计算器II - Leecode 227
题目
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。
注意: 不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
示例
示例 1:
输入: s = "3+2*2"
输出: 7
示例 2:
输入: s = " 3/2 "
输出: 1
示例 3:
输入: s = " 3+5 / 2 "
输出: 5
提示:
1 <= s.length <= 3 * 105s由整数和算符('+', '-', '*', '/')组成,中间由一些空格隔开s表示一个 有效表达式- 表达式中的所有整数都是非负整数,且在范围
[0, 231 - 1]内 - 题目数据保证答案是一个 32-bit 整数
解法
/**
* @param {string} s - 输入的数学表达式字符串
* @return {number} - 返回计算结果
*/
var calculate = function(s) {
const stack = []; // 用于存储中间计算结果的栈
let preOperator = '+'; // 上一个运算符,初始化为 '+'
let num = ''; // 当前数字的字符串形式
// 遍历字符串
for (let i = 0; i < s.length; i++) {
const char = s[i]; // 当前字符
// 如果是数字字符,则将其追加到当前数字字符串
if (!isNaN(char)) {
num += char;
}
// 如果遇到非数字字符或已经到字符串的最后一个字符
if (isNaN(char) || i === s.length - 1) {
switch (preOperator) { // 根据上一个运算符执行操作
case '+':
stack.push(+num); // 将当前数字压入栈
break;
case '-':
stack.push(-num); // 将当前数字的负值压入栈
break;
case '*':
stack.push(stack.pop() * num); // 弹出栈顶数字,与当前数字相乘后压入栈
break;
case '/':
// 弹出栈顶数字,与当前数字相除(向零取整)后压入栈
// Math.trunc(n) 返回数字的整数部分(向零取整),如果数字为负,则结果也为负数
stack.push(Math.trunc(stack.pop() / num));
break;
}
// 更新上一个运算符为当前字符
preOperator = char;
// 重置当前数字
num = '';
}
}
// 将栈中的所有数字相加,得到最终结果
return stack.reduce((acc, cur) => acc + cur, 0);
};
[困难] 高级计算器
题目
编写一个函数 StringCalculate(str),接受一个字符串参数 str,并对其中的数学表达式进行计算。双星号(**)表示幂运算。
例如:
- 如果输入是
"(2+(3-1)*3)**3",输出应该是512。 - 再比如,如果输入是
"(2-0)(6/2)",输出应该是6。
字符串中可能包含括号,所以必须正确按照算术规则对其进行评估。字符串将包含以下运算符:+、-、/、*、() 和 **。
如果字符串像这样:#/#*# 或 +#+(#)/#,那么应按照从左到右的顺序进行计算。例如:
- 对于
#/#*#,先除法再乘法。 - 对于
+#+(#)/#,先乘法,再除法,最后加法。
所有计算都保证不会产生小数结果,因此不需要考虑四舍五入的问题。
示例
示例 1:
输入: "6*(4/2)+3*1"
输出: 15
示例 2:
输入: "100*2**4"
输出: 1600
解法
抛砖引玉,我只是写了一种解法,也不一定完全正确。如果有更好的解法欢迎分享!
function stringCalculate(str) {
// 预处理字符串:
// 1. 处理数字后面直接跟左括号的情况:例如 6(4) → 6*(4)
// 2. 处理右括号后面直接跟左括号的情况:例如 (2)(3) → (2)*(3)
str = str.replace(/(\d+|\))\(/g, "$1*(");
// 辅助函数:计算乘除法,返回处理后的数组
function handleMulDiv(tokens) {
let stack = [];
let i = 0;
while (i < tokens.length) {
let token = tokens[i];
if (token === "*" || token === "/") {
let prev = stack.pop();
let next = tokens[i + 1];
if (token === "*") {
stack.push(prev * next);
} else {
stack.push(prev / next);
}
i++; // 跳过下一个操作数
} else {
stack.push(token);
}
i++;
}
return stack;
}
// 辅助函数:计算加减法,返回最终结果
function handleAddSub(tokens) {
let result = tokens[0];
for (let i = 1; i < tokens.length; i += 2) {
let operator = tokens[i];
let operand = tokens[i + 1];
if (operator === "+") {
result += operand;
} else if (operator === "-") {
result -= operand;
}
}
return result;
}
// 辅助函数:计算指数运算,返回处理后的数组
function handleExp(tokens) {
let stack = [];
let i = 0;
while (i < tokens.length) {
let token = tokens[i];
if (token === "**") {
let prev = stack.pop();
let next = tokens[i + 1];
stack.push(Math.pow(prev, next)); // 计算指数
i++; // 跳过下一个操作数
} else {
stack.push(token);
}
i++;
}
return stack;
}
// 辅助函数:递归地处理括号
function evaluate(expression) {
// 处理括号
let regex = /\(([^()]+)\)/;
while (regex.test(expression)) {
expression = expression.replace(regex, (match, subExpr) =>
evaluate(subExpr)
);
}
// 改进的token分割逻辑
let tokens = [];
let number = "";
let prevChar = "";
for (let i = 0; i < expression.length; i++) {
let char = expression[i];
// 处理数字和小数点
if (/[\d.]/.test(char)) {
number += char;
}
// 处理负号
else if (char === "-" && (i === 0 || /[\+\-\*\/\(]/.test(prevChar))) {
number = "-" + number;
}
// 处理运算符
else {
if (number !== "") {
tokens.push(Number(number));
number = "";
}
if (char !== " ") {
// 处理 ** 运算符
if (char === "*" && expression[i + 1] === "*") {
tokens.push("**");
i++;
} else {
tokens.push(char);
}
}
}
prevChar = char;
}
if (number !== "") {
tokens.push(Number(number));
}
// 处理指数运算符 (最优先)
tokens = handleExp(tokens);
// 处理乘除法
tokens = handleMulDiv(tokens);
// 处理加减法
return handleAddSub(tokens);
}
return evaluate(str);
}
// 测试用例
console.log(stringCalculate("(2+(3-1)*3)**3")); // 输出: 512
console.log(stringCalculate("(2-0)*(6/2)")); // 输出: 6
console.log(stringCalculate("(2-0)(6/2)")); // 输出: 6
console.log(stringCalculate("6(4/2)(6/2)+3*1")); // 输出: 39
console.log(stringCalculate("6*(4/2)+3*1")); // 输出: 15
console.log(stringCalculate("100*2**4")); // 输出: 1600