Intuition¶
Nothing about this game needs to be simulated. Only two facts about num matter:
how far apart the two halves' digit sums are, and how the '?' blanks are split
between them. Everything else — which blank a player picks, in what order — turns
out to be irrelevant under optimal play, so the answer collapses to a
constant-size arithmetic test after one pass over the string.
The reason is that Bob only wins on a knife edge. He needs the final sums to be exactly equal, while Alice wins on every other outcome. A single target against everything else is a fragile position, and it survives only when the blanks let Bob mirror every move Alice makes.
Both quantities can be tracked as signed running differences: add for the left half, subtract for the right. One number then carries the digit gap and another carries the blank imbalance, with no need to keep the two halves apart.
Approach: Parity and the Balance Point¶
Walk the string once, treating the left half as positive and the right half as negative:
sumDiff— digit sum of the left half minus digit sum of the right half.cntDiff— number of'?'in the left half minus the number in the right half.
Two rules then decide the game.
Rule 1: an odd blank imbalance means Alice wins¶
cntDiff and the total blank count qLeft + qRight always share the same parity,
because they differ by 2 * qRight. So testing cntDiff % 2 != 0 is exactly a test
for an odd total number of blanks.
When that total is odd the players alternate A, B, A, ... , A, so Alice takes
the final move. At that point one blank remains and the rest of the board is
fixed, meaning the two sums are already determined up to her single digit. Of the
ten digits available, at most one makes the sums equal; she writes any of the other
nine and wins.
Rule 2: otherwise Bob needs one exact value¶
With an even blank count, Bob wins iff
$$2 \cdot \text{sumDiff} + 9 \cdot \text{cntDiff} = 0$$
so Alice wins whenever that expression is non-zero — which is precisely the second half of the returned condition.
Where the 9 comes from¶
Group the blanks into pairs, which is possible because there is an even number of
them. Writing a for the left blank count and b for the right, and
taking $$a \le b$$ for concreteness:
across pairs, each holding one blank in the left half and one in the right.(b - a) / 2same-side pairs, each holding two blanks in the right half — the surplus thatcntDiffmeasures.
Bob's strategy is simply to close whatever pair Alice opens:
- Alice writes
dinto a cross pair → Bob writes the samedinto that pair's other blank, in the opposite half. Both sums rise byd, sosumDiffis unchanged. - Alice writes
dinto a same-side pair → Bob writes9 - dinto that pair's other blank. The pair contributes exactly9to the right half whatever Alice chose.
Alice always moves first within a pair and Bob always answers inside that same
pair, so every pair resolves this way. Cross pairs move sumDiff by 0; each of
the (b - a) / 2 same-side pairs drives it down by 9. The final difference is
therefore forced to
$$\text{sumDiff} - \frac{9 \cdot (b - a)}{2} = \text{sumDiff} + \frac{9 \cdot \text{cntDiff}}{2}$$
since $$\text{cntDiff} = a - b$$. Bob wins exactly when that lands on zero.
Why the formula is doubled¶
Setting the expression above to zero gives the balance point
$$\text{sumDiff} = -\frac{9 \cdot \text{cntDiff}}{2}$$
which involves a division by two. Multiplying both sides through by 2 clears it:
$$2 \cdot \text{sumDiff} + 9 \cdot \text{cntDiff} = 0$$
This is the form the code uses, and it is strictly better than dividing. cntDiff
is even on this branch so the division would be exact — but writing it as a
multiplication means there is no truncation to reason about at all, and in
particular no need to think about how each language rounds negative integer
division. The test stays pure integer arithmetic.
Why anything else loses for Bob¶
The pairing argument is what Bob's win depends on: mirroring drives the
difference to exactly one reachable value. If the doubled expression starts
non-zero, that forced landing point is not zero either, and Alice — who opens every
pair — can hold it away from zero by playing the extreme digits 0 and 9, which
Bob's single reply cannot fully absorb.
So the entire game reduces to:
cntDiff is odd -> Alice wins
even, 2*sumDiff + 9*cntDiff == 0 -> Bob wins
even, 2*sumDiff + 9*cntDiff != 0 -> Alice wins
Reading the sign of cntDiff¶
The signed convention keeps working in both directions with no special cases:
cntDiff < 0— the right half holds surplus blanks, so those pairs will add to the right. Bob needs the left half to start ahead, and the required value $$-9 \cdot \text{cntDiff} / 2$$ is then positive.cntDiff > 0— the surplus is on the left, so Bob needs the left half to start behind, and the requiredsumDiffis negative.cntDiff == 0— the blanks pair up across the halves and cancel, so Bob simply needssumDiff == 0.
Worked examples¶
num = "5023" → false¶
| position | char | half | effect |
|---|---|---|---|
| 0 | 5 |
left | sumDiff += 5 → 5 |
| 1 | 0 |
left | sumDiff += 0 → 5 |
| 2 | 2 |
right | sumDiff -= 2 → 3 |
| 3 | 3 |
right | sumDiff -= 3 → 0 |
Final sumDiff = 0, cntDiff = 0. The count is even, and
2 * 0 + 9 * 0 = 0, so Bob wins and the answer is false. This is the degenerate
case: no moves exist and the halves are already equal.
num = "25??" → true¶
| position | char | half | effect |
|---|---|---|---|
| 0 | 2 |
left | sumDiff += 2 → 2 |
| 1 | 5 |
left | sumDiff += 5 → 7 |
| 2 | ? |
right | cntDiff -= 1 → -1 |
| 3 | ? |
right | cntDiff -= 1 → -2 |
Final sumDiff = 7, cntDiff = -2, which is even, so Rule 2 applies:
2 * 7 + 9 * (-2) = 14 - 18 = -4, non-zero, so Alice wins.
Concretely: both blanks form one same-side pair in the right half. Whatever Alice
writes, Bob's best reply makes that pair total 9, giving 9 on the right against
7 on the left. Bob would need a left half of exactly 9 to survive.
num = "?3295???" → false¶
| position | char | half | effect |
|---|---|---|---|
| 0 | ? |
left | cntDiff += 1 → 1 |
| 1 | 3 |
left | sumDiff += 3 → 3 |
| 2 | 2 |
left | sumDiff += 2 → 5 |
| 3 | 9 |
left | sumDiff += 9 → 14 |
| 4 | 5 |
right | sumDiff -= 5 → 9 |
| 5 | ? |
right | cntDiff -= 1 → 0 |
| 6 | ? |
right | cntDiff -= 1 → -1 |
| 7 | ? |
right | cntDiff -= 1 → -2 |
Final sumDiff = 9, cntDiff = -2, even, and
2 * 9 + 9 * (-2) = 18 - 18 = 0, so Bob wins and the answer is false.
Reading Bob's play off the decomposition: the four blanks split into one cross pair
(the left ? with one right ?) and one same-side pair (the last two right ?).
The cross pair leaves sumDiff at 9; the same-side pair adds exactly 9 to the
right. Final sums are 14 + d on the left and 5 + d + 9 on the right for
whatever d Alice played in the cross pair — equal for every choice. That matches
the walkthrough in the statement, which ends 9+3+2+9 = 5+9+2+7.
Implementation notes¶
% 2 != 0, never% 2 == 1.cntDiffis a signed difference and is routinely negative. Go and Rust take the sign of the dividend, so-3 % 2is-1there, while Python floors and yields1. Comparing against0is correct in all three languages; comparing against1would silently break the Go and Rust versions on any input whose left half holds more blanks.- Short-circuit ordering. The parity test comes first, so the arithmetic check is only reached when the blank count is even — exactly the case the pairing argument covers.
- The Go loop needs Go 1.22+.
for i := range n / 2uses range-over-integer, added in Go 1.22; on older toolchains writefor i := 0; i < n/2; i++. - No overflow. With $$n \le 10^5$$ the digit sums stay below $$9 \cdot 10^5$$,
and doubling keeps the expression under $$2 \cdot 10^6$$ — comfortably inside
32-bit range, so Rust's
i32is safe.
Complexity¶
- Time complexity: $$O(n)$$ — one pass over
num, wherenis its length. - Space complexity: $$O(1)$$ — two integer accumulators.
Code¶
Go¶
func sumGame(num string) bool {
n := len(num)
sumDiff, cntDiff := 0, 0
for i := range n / 2 {
if num[i] == '?' {
cntDiff++
} else {
sumDiff += int(num[i] - '0')
}
}
for i := n/2; i < n; i++ {
if num[i] == '?' {
cntDiff--
} else {
sumDiff -= int(num[i] - '0')
}
}
return cntDiff % 2 != 0 || 2 * sumDiff + 9 * cntDiff != 0
}
Rust¶
impl Solution {
pub fn sum_game(num: String) -> bool {
let num = num.as_bytes();
let n = num.len();
let (mut sum_diff, mut cnt_diff) = (0, 0i32);
for i in 0..n/2 {
if num[i] == b'?' {
cnt_diff += 1;
} else {
sum_diff += (num[i] - b'0') as i32;
}
}
for i in n/2..n {
if num[i] == b'?' {
cnt_diff -= 1;
} else {
sum_diff -= (num[i] - b'0') as i32;
}
}
cnt_diff % 2 != 0 || 2 * sum_diff + 9 * cnt_diff != 0
}
}
Python¶
class Solution:
def sumGame(self, num: str) -> bool:
sum_diff, cnt_diff = 0, 0
n = len(num)
for i in range(0, n//2):
if num[i] == '?':
cnt_diff += 1
else:
sum_diff += int(num[i])
for i in range(n//2,n):
if num[i] == '?':
cnt_diff -= 1
else:
sum_diff -= int(num[i])
return cnt_diff % 2 != 0 or (2 * sum_diff + 9 * cnt_diff != 0)
Test cases¶
num |
sumDiff |
cntDiff |
parity | 2*sumDiff + 9*cntDiff |
result |
|---|---|---|---|---|---|
"5023" |
0 | 0 | even | 0 | false |
"25??" |
7 | -2 | even | -4 | true |
"?3295???" |
9 | -2 | even | 0 | false |
"99" |
0 | 0 | even | 0 | false |
"9?7?" |
2 | 0 | even | 4 | true |
"??00" |
0 | 2 | even | 18 | true |
"?0" |
0 | 1 | odd | (short-circuited) | true |
The characterisation was checked against an exhaustive memoised minimax over the full game tree: all 121 strings of length 2 and all 14641 of length 4 agree, as do 3000 random strings of length 6 and 8. The Go, Rust and Python versions were run independently and return identical verdicts on every one of those inputs.