NC16539 [NOIP2013]表达式求值

题目

题目描述

给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。

输入描述

输入仅有一行,为需要你计算的表达式,表达式中只包含数字、加法运算符“+”和乘法运算符“*”,且没有括号。
所有参与运算的数字均为 \(0\)\(2^{31}-1\) 之间的整数。
输入数据保证这一行只有 \(0\) ~ \(9\) 、+ 、*这12种字符。

输出描述

输出只有一行,包含一个整数,表示这个表达式的值。注意:当答案长度多于 \(4\) 位时,请只输出最后 \(4\) 位,前导 \(0\) 不输出。

示例1

输入

1+1*3+4

输出

8

说明

计算的结果为 \(8\) ,直接输出 \(8\)

示例2

输入

1+1234567890*1

输出

7891

说明

计算的结果为 \(1234567891\) ,输出后 \(4\) 位,即 \(7891\)

示例3

输入

1+1000000003*1

输出

4

说明

计算的结果为 \(1000000004\) ,输出后 \(4\) 位,即 \(4\)

备注

对于 \(30\%\) 的数据, \(0\) ≤表达式中加法运算符和乘法运算符的总数≤ \(100\)
对于 \(80\%\) 的数据, \(0\) ≤表达式中加法运算符和乘法运算符的总数≤ \(1000\)
对于 \(100\%\) 的数据,\(0\) ≤表达式中加法运算符和乘法运算符的总数≤ \(100000\)

题解

方法一

知识点:分治。

表达式的运算有优先级直接读取难以计算,但运算总有最后一步,通过找到最后一次的运算将表达式以这个符号拆成左右两部分,就可以展开递归进行相同的操作。每次只需要遍历一遍当前表达式,找到所有符号最后出现的位置,加号优先判断,如果加号没有位置那就看乘号,来决定下一次拆分,如果都没有的话那说明是个纯数字,就字符串转换数字即可。注意到结果保留末四位,等价于模 \(10000\),因为加法乘法在模意义下也具有意义。

此解法属于万能解法,可以十分方便处理所有运算符的一系列计算(包括但不限于加,减,乘,除,乘方,阶乘),而且可以通过改进变为可以带括号。平均复杂度 \(O(n\log n)\) ,但如果每次拆分都在最左最右,那么会退化到 \(O(n^2)\) ,而本题有连续加的特殊数据因此在加法处判断后立即break可以优化。但如果是乘法则无法直接break优化,因为无法确定后方是否有更小优先级的运算符,但本题没有连续乘法的数据qwq。

时间复杂度 平均:\(O(nlogn)\) 最差: \(O(n^2)\)

空间复杂度 \(O(n)\)

方法二

知识点:模拟。

发现连续乘法的数字可以连续算出放入临时变量,然后加入最终答案,因为只有加法和乘法。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>

using namespace std;

const int mod = 1e4;

string s;

int StoI(int l, int r) {
    int ans = 0;
    for (int i = l;i <= r;i++) ans = (10 * ans + s[i] - '0') % mod;
    return ans;
}

int calc(int l, int r) {
    int pos[2] = { -1,-1 };///+,*
    for (int i = l;i <= r;i++) {///找到最后一次出现的符号(因为没有括号直接找最后一次的)
        if (s[i] == '+') {
            pos[0] = i;
            break;
        }///找到+可以直接退出,避免最差复杂度
        else if (s[i] == '*') pos[1] = i;
    }
    if (!~pos[0] && !~pos[1]) return StoI(l, r);///纯数
    else if (~pos[0]) return (calc(l, pos[0] - 1) + calc(pos[0] + 1, r)) % mod;
    else if (~pos[1]) return calc(l, pos[1] - 1) * calc(pos[1] + 1, r) % mod;
    return 0;///过编译
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> s;
    cout << calc(0, s.length() - 1) << '\n';
    return 0;
}

方法二

#include <bits/stdc++.h>

using namespace std;

const int mod = 1e4;

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    string s;
    cin >> s;
    s += '+';///终止最后一段
    int ans = 0;
    for (int i = 0, tmp = 0, last = 1;i < s.length();i++) {///tmp指一个数字,last指一个乘法数块
        if ('0' <= s[i] && s[i] <= '9') tmp = (tmp * 10 + s[i] - '0') % mod;///更新tmp
        else if (s[i] == '+') {///把tmp乘进last,更新ans、tmp、last
            last = last * tmp % mod;
            ans = (ans + last) % mod;
            tmp = 0;
            last = 1;
        }
        else if (s[i] == '*') {///把tmp乘进last,更新tmp
            last = last * tmp % mod;
            tmp = 0;
        }
    }
    cout << ans << '\n';
    return 0;
}
posted @ 2022-06-23 11:41  空白菌  阅读(486)  评论(0编辑  收藏  举报