You are given a string s
that contains digits 0-9
, addition symbols '+'
, and multiplication symbols '*'
only, representing a valid math expression of single digit numbers (e.g., 3+5*2
). This expression was given to n
elementary school students. The students were instructed to get the answer of the expression by following this order of operations:
You are given an integer array answers
of length n
, which are the submitted answers of the students in no particular order. You are asked to grade the answers
, by following these rules:
5
points;2
points;0
points.Return the sum of the points of the students.
Example 1:
Input: s = "7+3*1*2", answers = [20,13,42] Output: 7 Explanation: As illustrated above, the correct answer of the expression is 13, therefore one student is rewarded 5 points: [20,13,42] A student might have applied the operators in this wrong order: ((7+3)*1)*2 = 20. Therefore one student is rewarded 2 points: [20,13,42] The points for the students are: [2,5,0]. The sum of the points is 2+5+0=7.
Example 2:
Input: s = "3+5*2", answers = [13,0,10,13,13,16,16] Output: 19 Explanation: The correct answer of the expression is 13, therefore three students are rewarded 5 points each: [13,0,10,13,13,16,16] A student might have applied the operators in this wrong order: ((3+5)*2 = 16. Therefore two students are rewarded 2 points: [13,0,10,13,13,16,16] The points for the students are: [5,0,0,5,5,2,2]. The sum of the points is 5+0+0+5+5+2+2=19.
Example 3:
Input: s = "6+0*1", answers = [12,9,6,4,8,6] Output: 10 Explanation: The correct answer of the expression is 6. If a student had incorrectly done (6+0)*1, the answer would also be 6. By the rules of grading, the students will still be rewarded 5 points (as they got the correct answer), not 2 points. The points for the students are: [0,0,5,0,0,5]. The sum of the points is 10.
Constraints:
3 <= s.length <= 31
s
represents a valid expression that contains only digits 0-9
, '+'
, and '*'
only.[0, 9]
.1 <=
The count of all operators ('+'
and '*'
) in the math expression <= 15
[0, 1000]
.n == answers.length
1 <= n <= 104
0 <= answers[i] <= 1000
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this problem is like trying every single possible way to calculate the math expression. We explore all combinations of operations, regardless of order, and then compare the results to the student's answer to calculate a score.
Here's how the algorithm would work step-by-step:
def score_of_students(expression, correct_answer, student_answer):
possible_results = set()
numbers = []
operators = []
number_string = ''
for character in expression:
if character.isdigit():
number_string += character
else:
numbers.append(int(number_string))
number_string = ''
operators.append(character)
numbers.append(int(number_string))
def calculate_possible_results(current_numbers, current_operators, current_result):
if not current_operators:
possible_results.add(current_result)
return
for i in range(len(current_operators)):
next_numbers = current_numbers[:]
next_operators = current_operators[:]
first_number = next_numbers.pop(i)
second_number = next_numbers.pop(i)
operator = next_operators.pop(i)
if operator == '+':
new_result = first_number + second_number
else:
new_result = first_number * second_number
if new_result <= 1000:
calculate_possible_results(next_numbers[:i] + [new_result] + next_numbers[i:], next_operators, current_result)
calculate_possible_results(numbers, operators, 0)
# Calculate the correct answer using eval.
actual_result = eval(expression)
if student_answer == actual_result:
return 5
# Check if the student's answer is among possible wrong answers.
if student_answer in possible_results:
return 2
return 0
To efficiently determine the possible scores, we use a strategy where we build up all possible correct and incorrect answers from smaller sub-expressions. This avoids exhaustively checking every possible calculation order.
Here's how the algorithm would work step-by-step:
def score_of_students(math_expression, student_answers):
tokens = []
number = 0
for character in math_expression:
if character.isdigit():
number = number * 10 + int(character)
else:
tokens.append(number)
tokens.append(character)
number = 0
tokens.append(number)
def calculate_correct_answer(expression_tokens):
operands = []
operators = []
for token in expression_tokens:
if isinstance(token, int):
operands.append(token)
else:
while operators and operators[-1] == '*' and token != '*':
operator = operators.pop()
operand2 = operands.pop()
operand1 = operands.pop()
operands.append(operand1 * operand2)
operators.append(token)
while operators:
operator = operators.pop()
operand2 = operands.pop()
operand1 = operands.pop()
if operator == '+':
operands.append(operand1 + operand2)
else:
operands.append(operand1 * operand2)
return operands[0]
correct_answer = calculate_correct_answer(tokens)
# DP table to store possible results for each sub-expression
possible_results = {}
def calculate_possible_results(start, end):
if (start, end) in possible_results:
return possible_results[(start, end)]
if start == end:
possible_results[(start, end)] = {tokens[start]}
return possible_results[(start, end)]
results = set()
# Iterate through all possible split points
for i in range(start + 1, end, 2):
operator = tokens[i]
# Compute possible results for left and right sub-expressions
left_results = calculate_possible_results(start, i - 1)
right_results = calculate_possible_results(i + 1, end)
# Combine results using the operator
for left_result in left_results:
for right_result in right_results:
if operator == '+':
result = left_result + right_result
else:
result = left_result * right_result
if 0 <= result <= 1000:
results.add(result)
possible_results[(start, end)] = results
return results
all_possible_answers = calculate_possible_results(0, len(tokens) - 1)
total_score = 0
for answer in student_answers:
if answer == correct_answer:
total_score += 5
elif answer in all_possible_answers:
total_score += 2
# Return the calculated score.
return total_score
Case | How to Handle |
---|---|
Empty expression string | Return 0 since no correct answer can be computed and no student answers are valid. |
Null expression string or null student answers list | Throw an IllegalArgumentException or return 0 after checking for null inputs. |
Expression string contains invalid characters other than digits, '+', and '*' | Throw an IllegalArgumentException after validating all characters in the input string. |
Very long expression string leading to integer overflow | Use dynamic programming with memoization to avoid recomputation and handle potential overflows during intermediate calculations (values are bounded to 1000). |
Expression with consecutive operators ('++' or '**' or '+*') | Throw an IllegalArgumentException after validating that no consecutive operators exist. |
Student answers list contains duplicate answers | The scoring logic should handle duplicates by incrementing the score accordingly for each duplicate answer. |
Student answers containing values outside the 0-1000 range, although intermediate calculations should always be in range | The condition `0 <= value <= 1000` is crucial during the calculation of possible parenthesizations, so we only consider student answers falling within the range 0-1000 when checking student answers for being an alternative parenthesization. |
Expression that results in a correct answer outside the range [0, 1000] | The problem statement specifies that incorrect alternative parenthesizations must result in values within the specified range, but the *correct* value could exceed it; compute it as part of alternative parenthesization but ignore for awarding 5 points. |