本文已参与「新人创作礼」活动,一起开启掘金创作之路。
痛点
题目是:给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。 比如:
输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23
这题有点类似编译器设计,特点是状态很多,很容易迷失在if-else中,所以使用有限状态机DFA来解决
思路详解
DFA(deterministic finite automation)就是一系列状态的迁移,大家可以试着画下图,就如下4种状态转来转去,很简单的
上图中我画了起始态的迁移示例(红色字体),符号态的迁移示例(蓝色字体),其他不画了,道理一样的,大家也可以自己画下
状态
0-start 世界起始态; 1-sig 置加号或减号的状态; 2-number 输入数字时状态; 3-end 世界结束态;
迁移条件:输入的字符的种类驱动了状态迁移
0是( 1 +或- 2是数字 3是) 4是其他非法字符
注意事项:
- 空格事先去掉,简化代码
- 起始态和结束态,不仅是字符串开始和结束,"("号也会开始一个新的世界,原来的世界入栈
代码(附带详细注解)
/**
* @param {string} s
* @return {number}
*/
const DFA = [
[0, 1, 2, 3, 3], //当状态是起始态时,遇到各种字符类型所跳转到的下个状态,比如0是(,遇到0还是起始态,只不过是下个世界的
[0, 3, 2, 3, 3],
[3, 1, 2, 3, 3],
[3, 1, 3, 3, 3]
];
//字符分类子函数:返回字符的类型 0是( 1 +或- 2是数字 3是) 4是其他非法字符
const classify = function(c) {
if (c === '(') return 0;
else if (c === '+' || c === '-') return 1;
else if (/[0-9]/.test(c)) return 2;
else if (c === ')') return 3;
else return 4;
}
var calculate = function(s) {
let accm = 0; //本世界积累的值
let flag = true; //标志,加号,减号
let val = 0; //输入数字过程中存储的值
let status = 0;
const p = [];
for (let c of s.replaceAll(" ", "")) {
const charType = classify(c);
const transit = DFA[status][charType];
if (transit === 0) { //世界进入起始态动作
p.push({accm, flag});
accm = 0;
flag = true;
val = 0;
} else if (transit === 1) { //输入符号时动作
accm += flag ? val : -val
flag = (c === '+') ? true : false;
val = 0;
} else if (transit === 2) { //输入数字时动作
val = val * 10 + Number(c);
} else { //这个世界结束时动作
accm += flag ? val : -val;
const previous = p.pop();
accm = previous.accm + (previous.flag ? accm : -accm);
flag = true;
val = 0;
}
status = transit;
}
return accm + (flag ? val : -val);
};