每日一算法题-字符串计算器

81 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

一、题目

image.png

#include <iostream>
using namespace std;

int cal(string s)
{
    
}

int main(int, char**)
{
    string s = "2*((3-4)*5)";
    cout << s << '=' << cal(s) << endl;
    s = "1+2*3+4*5+6";
    cout << s << '=' << cal(s) << endl;
    return 0;
}

二、分析

运算优先级:

一级: 括号
二级: 乘法
三级: 加减

由题意分析到,这道题需要用到分治,可以将一整个表达式通过运算优先级分成多个可以独立运算的子问题,独立求解之后再汇总到一起。
这里在拆分子问题的时候,要注意到是对字符串的解析,所以需要用到结束符的判定,一级是用左括号开始,右括号结束,而二级是*开始,拿到一个有效的数就算结束,这里的判定要注意,如何拿到一个有效的数也是一个隐藏的坑。
因为*是没有结束符的,所以只能通过拿到下一个有效数之后先行进行运算之后,将之化简为三级运算就可以如履平地了。
由上分析可知,我们的运算要先拿到运算符,然后再拿到下一个有效数之后才能开始,所以拿到第一个有效数之后也要当成第二个有效数,0加任何数等于原数,所以要预先假设拥有了第一个有效数0,第一个运算符+。

三、模拟

比如这个表达式:

1+2*(3+4)*5+6

就可以分解成:

1. 3+4 = 7
2. 2*7 = 14
3. 14*5 = 70
4. 1+70 = 71
5. 71+6 = 77

四、实现

int calDfs(string s, size_t& cur)
{
    list<int> datas;
    int num = 0;
    char opera = '+';
    char ch = 0;

    for(; cur < s.size(); ++cur){
        ch = s.at(cur);
        if(ch >= '0' && ch <= '9'){
            num = num * 10 + ch - '0';
            if(cur + 1 < s.size()){
                continue;
            }
        }
        if(ch == '('){
            ++cur;
            num = calDfs(s, cur);
            if(cur + 1 < s.size()){
                continue;
            }
        }
        switch (opera) {
        case '+':
            datas.push_back(num);
            break;
        case '-':
            datas.push_back(-num);
            break;
        case '*':
             int value = datas.back() * num;
             datas.pop_back();
             datas.push_back(value);
            break;
        }
        num = 0;
        if(ch == ')') break;
        else opera = ch;
    }

    int result = 0;
    for(int data : datas){
        result += data;
    }
    return result;
}
int cal(string s)
{
    size_t cur = 0;
    return calDfs(s, cur);
}

五、结言

这是最简单的四则运算,主要的难点是从字符串中去分割。

创作不易,留个赞再走吧!如果对文章内容有任何指正,欢迎评论!