LeetCode224基本计算器用DFA实现超简洁

172 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

痛点

题目是:给你一个字符串表达式 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是其他非法字符

注意事项:
  1. 空格事先去掉,简化代码
  2. 起始态和结束态,不仅是字符串开始和结束,"("号也会开始一个新的世界,原来的世界入栈

代码(附带详细注解)

/**
 * @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);
};