LeetCode 题解工作台
解出数学表达式的学生分数
给你一个字符串 s ,它 只 包含数字 0-9 ,加法运算符 '+' 和乘法运算符 '*' ,这个字符串表示一个 合法 的只含有 个位数 数字 的数学表达式(比方说 3+5*2 )。有 n 位小学生将计算这个数学表达式,并遵循如下 运算顺序 : 按照 从左到右 的顺序计算 乘法 ,然后 按照 从左到…
6
题型
5
代码语言
3
相关题
当前训练重点
困难 · 状态·转移·动态规划
答案摘要
我们先设计一个函数 ,用于计算一个合法的只含有个位数数字的数学表达式的结果。那么正确答案就是 $x = cal(s)$。 我们记字符串 的长度为 ,那么 中的数字个数为 $m = \frac{n+1}{2}$。
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路
题目描述
给你一个字符串 s ,它 只 包含数字 0-9 ,加法运算符 '+' 和乘法运算符 '*' ,这个字符串表示一个 合法 的只含有 个位数数字 的数学表达式(比方说 3+5*2)。有 n 位小学生将计算这个数学表达式,并遵循如下 运算顺序 :
- 按照 从左到右 的顺序计算 乘法 ,然后
- 按照 从左到右 的顺序计算 加法 。
给你一个长度为 n 的整数数组 answers ,表示每位学生提交的答案。你的任务是给 answer 数组按照如下 规则 打分:
- 如果一位学生的答案 等于 表达式的正确结果,这位学生将得到
5分。 - 否则,如果答案由 一处或多处错误的运算顺序 计算得到,那么这位学生能得到
2分。 - 否则,这位学生将得到
0分。
请你返回所有学生的分数和。
示例 1:

输入:s = "7+3*1*2", answers = [20,13,42] 输出:7 解释:如上图所示,正确答案为 13 ,因此有一位学生得分为 5 分:[20,13,42] 。 一位学生可能通过错误的运算顺序得到结果 20 :7+3=10,10*1=10,10*2=20 。所以这位学生得分为 2 分:[20,13,42] 。 所有学生得分分别为:[2,5,0] 。所有得分之和为 2+5+0=7 。
示例 2:
输入:s = "3+5*2", answers = [13,0,10,13,13,16,16] 输出:19 解释:表达式的正确结果为 13 ,所以有 3 位学生得到 5 分:[13,0,10,13,13,16,16] 。 学生可能通过错误的运算顺序得到结果 16 :3+5=8,8*2=16 。所以两位学生得到 2 分:[13,0,10,13,13,16,16] 。 所有学生得分分别为:[5,0,0,5,5,2,2] 。所有得分之和为 5+0+0+5+5+2+2=19 。
示例 3:
输入:s = "6+0*1", answers = [12,9,6,4,8,6] 输出:10 解释:表达式的正确结果为 6 。 如果一位学生通过错误的运算顺序计算该表达式,结果仍为 6 。 根据打分规则,运算顺序错误的学生也将得到 5 分(因为他们仍然得到了正确的结果),而不是 2 分。 所有学生得分分别为:[0,0,5,0,0,5] 。所有得分之和为 10 分。
提示:
3 <= s.length <= 31s表示一个只包含0-9,'+'和'*'的合法表达式。- 表达式中所有整数运算数字都在闭区间
[0, 9]以内。 1 <=数学表达式中所有运算符数目('+'和'*')<= 15- 测试数据保证正确表达式结果在范围
[0, 1000]以内。 - 测试用例保证乘法中间步骤中的值永远不会超过
109。 n == answers.length1 <= n <= 1040 <= answers[i] <= 1000
解题思路
方法一:动态规划(区间 DP)
我们先设计一个函数 ,用于计算一个合法的只含有个位数数字的数学表达式的结果。那么正确答案就是 。
我们记字符串 的长度为 ,那么 中的数字个数为 。
我们定义 表示选择 中的第 个数字到第 个数字(下标从 开始)这一段数字,计算出的结果可能的取值。初始时 表示选择第 个数字,结果只能是这个数字本身,即 (即第 个数字映射到字符串 中的下标为 的字符)。
接下来,我们从大到小枚举 ,然后从小到大枚举 ,我们需要求出区间 所有数字运算的结果可能的取值。我们在区间 中枚举分界点 ,那么 可以由 和 通过运算符 得到。因此我们可以得到如下状态转移方程:
其中 表示运算符,即 。
那么字符串 所有数字运算的结果可能的取值就是 。
最后,我们统计答案。我们用一个数组 统计答案数组 中每个答案出现的次数。如果答案等于 ,那么这个学生得到 分,否则如果答案在 中,那么这个学生得到 分。遍历 ,统计答案即可。
时间复杂度 ,空间复杂度 。其中 是答案可能的最大值,而 是字符串 的长度中数字的个数。
class Solution:
def scoreOfStudents(self, s: str, answers: List[int]) -> int:
def cal(s: str) -> int:
res, pre = 0, int(s[0])
for i in range(1, n, 2):
if s[i] == "*":
pre *= int(s[i + 1])
else:
res += pre
pre = int(s[i + 1])
res += pre
return res
n = len(s)
x = cal(s)
m = (n + 1) >> 1
f = [[set() for _ in range(m)] for _ in range(m)]
for i in range(m):
f[i][i] = {int(s[i << 1])}
for i in range(m - 1, -1, -1):
for j in range(i, m):
for k in range(i, j):
for l in f[i][k]:
for r in f[k + 1][j]:
if s[k << 1 | 1] == "+" and l + r <= 1000:
f[i][j].add(l + r)
elif s[k << 1 | 1] == "*" and l * r <= 1000:
f[i][j].add(l * r)
cnt = Counter(answers)
ans = cnt[x] * 5
for k, v in cnt.items():
if k != x and k in f[0][m - 1]:
ans += v << 1
return ans
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | complexity is O(4^k * n) where k is the number of operators, due to generating all possible subexpression evaluations. Space complexity is O(k^2) for memoization storage of subexpression results. |
| 空间 | Depends on the final approach |
面试官常问的追问
外企场景- question_mark
Are you considering operator precedence and all alternative evaluation orders?
- question_mark
How are you memoizing intermediate results to prevent recomputation?
- question_mark
Can you handle overlapping subexpressions efficiently using DP?
常见陷阱
外企场景- error
Forgetting that students may apply operators in non-standard order, giving alternative valid results.
- error
Recomputing subexpression values without memoization, causing exponential time.
- error
Assuming all answers outside the correct one get zero points, ignoring partial scoring rules.
进阶变体
外企场景- arrow_right_alt
Expressions may include subtraction or division operators with similar scoring rules.
- arrow_right_alt
Multiple-digit numbers instead of single-digit, requiring careful parsing.
- arrow_right_alt
Changing point system to reward only top-k alternative answers instead of all.