LeetCode 3921 - Score Validator

This problem asks us to simulate a simple scoring system while processing a sequence of event strings from left to right. We are given an array called events, where each element represents one event.

LeetCode Problem 3921

Difficulty: 🟢 Easy
Topics: Array, String, Simulation

Solution

LeetCode 3921 - Score Validator

Problem Understanding

This problem asks us to simulate a simple scoring system while processing a sequence of event strings from left to right.

We are given an array called events, where each element represents one event. Two variables are maintained throughout the simulation:

  • score, initially 0
  • counter, initially 0

Each event affects these variables according to predefined rules:

  • "0", "1", "2", "3", "4", and "6" add their numeric value to score.
  • "W" increases counter by 1 and does not affect score.
  • "WD" adds 1 to score.
  • "NB" adds 1 to score.

The events must be processed in order. However, processing does not always continue until the end of the array. We stop as soon as one of the following conditions becomes true:

  1. Every event has been processed.
  2. counter reaches 10.

The final answer is an array containing:

[score, counter]

where score is the total accumulated score and counter is the final counter value.

The constraints are very small:

  • 1 <= events.length <= 1000
  • Every event is guaranteed to be one of the valid strings listed in the statement.

Because there are at most 1000 events, even a straightforward simulation is easily fast enough.

Some important edge cases include situations where the counter reaches 10 before the array ends, arrays consisting entirely of scoring events with no "W" events, and arrays where the first few events immediately push the counter to 10. The problem guarantees that every event string is valid, so we never need to handle unexpected input values.

Approaches

Brute Force Approach

The most direct approach is to simulate the process exactly as described.

We initialize score = 0 and counter = 0, then iterate through the array from left to right. For each event, we apply the corresponding rule:

  • Add numeric values for score events.
  • Increment the counter for "W".
  • Add one point for "WD" and "NB".

After processing each event, we check whether the counter has reached 10. If it has, we stop immediately and ignore all remaining events.

This approach is correct because it follows the problem statement exactly. Since each event is examined once, it is already efficient enough for the given constraints.

Key Insight

There is no hidden optimization or advanced data structure required. The problem is fundamentally a simulation problem.

The key observation is that each event affects only the current state (score and counter), and the result depends on processing events in order. Therefore, a single linear scan through the array is both natural and optimal.

Approach Comparison

Approach Time Complexity Space Complexity Notes
Brute Force O(n) O(1) Simulate every event sequentially
Optimal O(n) O(1) Same simulation, already optimal

Algorithm Walkthrough

  1. Initialize score = 0 and counter = 0.
  2. Iterate through each event in events from left to right.
  3. For the current event:
  • If it is "W", increment counter by 1.
  • If it is "WD" or "NB", add 1 to score.
  • Otherwise, the event is one of the digit strings, so convert it to an integer and add it to score.
  1. After processing the event, check whether counter has reached 10.
  2. If counter == 10, stop processing immediately because the problem explicitly states that processing ends when the counter becomes 10.
  3. Continue until either all events have been processed or processing stops early.
  4. Return [score, counter].

Why it works

The algorithm maintains the exact state required by the problem definition. After processing each event, score equals the total score contributed by all processed scoring events, and counter equals the number of processed "W" events. Since events are handled in order and processing stops immediately when counter reaches 10, the final state exactly matches the specification. Therefore, the returned result is correct.

Python Solution

class Solution:
    def scoreValidator(self, events: list[str]) -> list[int]:
        score = 0
        counter = 0

        for event in events:
            if event == "W":
                counter += 1
            elif event == "WD" or event == "NB":
                score += 1
            else:
                score += int(event)

            if counter == 10:
                break

        return [score, counter]

The implementation directly follows the simulation described in the algorithm.

The variables score and counter store the current state. The loop processes each event one at a time. A series of conditional checks determines which rule applies to the current event. Numeric events are converted using int(event) because the problem guarantees that any remaining valid string in this branch is one of the allowed digit values.

After every event is processed, the code checks whether the counter has reached 10. If so, break immediately terminates the loop, ensuring that all remaining events are ignored exactly as required.

Finally, the method returns the two values in the requested format.

Go Solution

func scoreValidator(events []string) []int {
	score := 0
	counter := 0

	for _, event := range events {
		if event == "W" {
			counter++
		} else if event == "WD" || event == "NB" {
			score++
		} else {
			score += int(event[0] - '0')
		}

		if counter == 10 {
			break
		}
	}

	return []int{score, counter}
}

The Go implementation follows the same logic as the Python version.

One small Go-specific optimization is that numeric events are single-character strings, so instead of using string-to-integer conversion functions, we can obtain the numeric value using:

int(event[0] - '0')

This avoids additional parsing overhead. Integer overflow is not a concern because the maximum possible score is very small relative to Go's integer range. The returned result is represented as a slice containing exactly two integers.

Worked Examples

Example 1

Input:

["1","4","W","6","WD"]
Event Score Before Counter Before Action Score After Counter After
"1" 0 0 Add 1 1 0
"4" 1 0 Add 4 5 0
"W" 5 0 Counter +1 5 1
"6" 5 1 Add 6 11 1
"WD" 11 1 Add 1 12 1

Final result:

[12, 1]

Example 2

Input:

["WD","NB","0","4","4"]
Event Score Before Counter Before Action Score After Counter After
"WD" 0 0 Add 1 1 0
"NB" 1 0 Add 1 2 0
"0" 2 0 Add 0 2 0
"4" 2 0 Add 4 6 0
"4" 6 0 Add 4 10 0

Final result:

[10, 0]

Example 3

Input:

["W","W","W","W","W","W","W","W","W","W","W"]
Step Event Score Counter
1 W 0 1
2 W 0 2
3 W 0 3
4 W 0 4
5 W 0 5
6 W 0 6
7 W 0 7
8 W 0 8
9 W 0 9
10 W 0 10

At this point the counter reaches 10, so processing stops immediately.

Final result:

[0, 10]

Complexity Analysis

Measure Complexity Explanation
Time O(n) Each event is processed at most once
Space O(1) Only a few integer variables are stored

The algorithm performs a single pass through the input array. Every event requires only constant-time work, so the running time is linear in the number of events. The amount of extra memory remains constant regardless of input size because only score and counter are maintained.

Test Cases

sol = Solution()

assert sol.scoreValidator(["1", "4", "W", "6", "WD"]) == [12, 1]  # Example 1
assert sol.scoreValidator(["WD", "NB", "0", "4", "4"]) == [10, 0]  # Example 2
assert sol.scoreValidator(["W"] * 11) == [0, 10]  # Example 3, early stop

assert sol.scoreValidator(["0"]) == [0, 0]  # Single zero event
assert sol.scoreValidator(["6"]) == [6, 0]  # Single scoring event
assert sol.scoreValidator(["WD"]) == [1, 0]  # Single WD event
assert sol.scoreValidator(["NB"]) == [1, 0]  # Single NB event

assert sol.scoreValidator(["W"]) == [0, 1]  # Single wicket

assert sol.scoreValidator(
    ["W","W","W","W","W","W","W","W","W","W","6"]
) == [0, 10]  # Events after tenth W ignored

assert sol.scoreValidator(
    ["1","2","3","4","6"]
) == [16, 0]  # All numeric scoring events

assert sol.scoreValidator(
    ["WD","NB","WD","NB"]
) == [4, 0]  # Only bonus scoring events

assert sol.scoreValidator(
    ["6","W","4","WD","NB","2"]
) == [14, 1]  # Mixed events

assert sol.scoreValidator(
    ["W"] * 10
) == [0, 10]  # Counter reaches exactly 10 at end

assert sol.scoreValidator(
    ["W"] * 10 + ["6", "6", "6"]
) == [0, 10]  # Remaining events ignored after stop

Test Summary

Test Why
["1","4","W","6","WD"] Validates basic mixed processing
["WD","NB","0","4","4"] Validates bonus and numeric scoring
["W"] * 11 Validates early termination
["0"] Smallest scoring value
["6"] Largest scoring value
["WD"] Single bonus event
["NB"] Single bonus event variant
["W"] Single counter increment
Ten W events then score events Ensures ignored suffix after stop
All numeric events Verifies digit handling
All bonus events Verifies repeated score additions
Mixed event sequence General correctness test
Exactly ten W events Boundary condition
Ten W events plus extras Early stop correctness

Edge Cases

Counter Reaches 10 Before the End of the Array

A common bug is continuing to process events even after the counter reaches 10. For example:

["W","W","W","W","W","W","W","W","W","W","6"]

The final "6" must be ignored. The implementation handles this by checking counter == 10 after every processed event and immediately breaking out of the loop.

Arrays With No "W" Events

An input such as:

["6","4","WD","NB"]

never increases the counter. In this case, every event should be processed and the final counter should remain zero. The algorithm naturally handles this because the stopping condition is never triggered.

The Tenth "W" Is the Final Processed Event

Consider:

["1","W","W","W","W","W","W","W","W","W","W"]

The tenth "W" must still be processed because it is the event that causes the counter to become 10. Only events after that point should be ignored. Since the algorithm updates the counter first and checks the stopping condition afterward, the tenth "W" is counted correctly before processing stops.

Numeric Event "0"

The string "0" is a valid scoring event but contributes zero points. A careless implementation might treat it as a special value or ignore it. Converting it with int(event) in Python or event[0] - '0' in Go correctly adds zero to the score without affecting the result.