Problem
Given an equation, represented by words on the left side and the result on the right side.
You need to check if the equation is solvable under the following rules:
- Each character is decoded as one digit (0 - 9).
- No two characters can map to the same digit.
- Each
words[i]andresultare decoded as one number without leading zeros. - Sum of numbers on the left side (
words) will equal to the number on the right side (result).
Return true if the equation is solvable, otherwise return false.
Examples
Example 1
| |
Example 2
| |
Example 3
| |
Constraints
2 <= words.length <= 51 <= words[i].length, result.length <= 7words[i], resultcontain only uppercase English letters.- The number of different characters used in the expression is at most
10.
Solution
Method 1 – Coefficient Summation Backtracking (Baseline)
Intuition
Treat each letter as a variable with a linear coefficient equal to the (signed) sum of its positional weights across all addend words minus those in the result. If we assign digits to letters, the total weighted sum must be zero. This converts per-assignment validation into a single final sum check, allowing pruning only on simple constraints (used digits, leading zeros) but not early arithmetic inconsistencies column-by-column.
Approach
- Collect unique letters; if > 10 → impossible.
- Build
coeff[c]: for each addend word add10^kfor position k from right; for result subtract10^k. - Record leading (non-zero) letters of all multi-length words (including result) to enforce non-zero digit.
- DFS over list of letters assigning unused digits (skipping 0 for leading letters).
- At leaf, compute total = Σ coeff[c] * digit[c]; success if total == 0.
This does not prune partial sums; worst-case explores nearly all permutations.
Code
| |
| |
| |
| |
| |
| |
| |
Complexity
- ⏰ Time complexity:
O(10^n * L)where n is the number of unique letters (≤10), L is the total length of all words. - 🧺 Space complexity:
O(n)for recursion and mappings.
Method 2 – Column-wise Carry Backtracking (Pruned DFS)
Intuition
We can prune vastly more by simulating the addition column by column (least significant to most), carrying over as in grade-school arithmetic. At each column we only consider the digits for letters that appear in that column whose digits are still unassigned; once all addend letters in a column are resolved we know the required digit (and carry) for the result letter—reject early if conflicting. This avoids exploring assignments that would only later violate the linear sum.
Approach
Let maxLen = max(len(word) for word in words + [result]).
Recursive function dfs(col, rowIndex, carry):
- If
col == maxLen→ success ifcarry == 0. - If
rowIndex < len(words): we are processing addend rows at current column.- If
col >= len(words[rowIndex])(word shorter) → just move to next row. - Else consider letter
ch = words[rowIndex][-1-col]:- If assigned: accumulate its digit into a running
columnSumand move to next row. - Else iterate candidate digits (skip used / leading-zero). Assign, recurse; backtrack.
- If assigned: accumulate its digit into a running
- If
- If
rowIndex == len(words): handle result letter at this column.- Compute
expected = columnSum + carry. - Required digit
d = expected % 10, new carrync = expected / 10. - Let
rch = result[-1-col]if column exists else treat as overflow → fail unless no remaining digits and expected forms exact leftover carry. - If
rchalready assigned: must matchd, then recurse to next column withdfs(col+1, 0, nc). - Else assign digit
d(respect leading-zero rule), recurse, backtrack.
- Compute
We maintain per-column columnSum via function argument or closure accumulation.
Ordering letters by usage frequency can further prune, but core column recursion already slashes search space.
Worked Example (SEND + MORE = MONEY)
Key early deductions without writing any full permutation:
- Result has one extra digit → final carry out of the highest processed column must be
1, soM = 1(and cannot be any other digit). This immediately marks a digit as used and enforces a future carry. - Column equations right→left (with carry
c):- Units:
D + E + c ≡ Y (mod 10); new carry⌊(D+E+c)/10⌋. - Tens:
N + R + c ≡ E (mod 10). - Hundreds:
E + O + c ≡ N (mod 10). - Thousands:
S + M + c ≡ O (mod 10)(withMalready fixed =1). - Ten‑Thousands: leftover carry must equal
M(which is 1) → consistency check.
- Units:
- Leading‑zero rule applies to
S,M, and the result’s first letter (M). - The Method 2 DFS enforces exactly these modular constraints on the fly: once all addend letters in a column are assigned, the result letter digit is forced; mismatch → immediate backtrack.
This concise reasoning is pedagogical; we avoid a duplicate specialized solver (the generic Method 2 code already handles the two‑addend case). For classroom exposition you can print the modular equations; implementation stays unchanged.
Pseudocode
| |
Code
| |
| |
| |
Complexity
- ⏰ Time complexity:
O(branch^C)whereC= number of columns; in practice far less than10^ndue to early carry pruning (often milliseconds for typical inputs). Worst theoretical still exponential in unique letters. - 🧺 Space complexity:
O(n + depth)– assignment arrays plus recursion stack depth up tomaxLen.
Method Comparison
| Method | Idea | Pruning | When to Teach | Time (practical) | Space |
|---|---|---|---|---|---|
| 1 Coefficient Backtracking | Linear sum = 0; assign all then check | Minimal (only leading + used digits) | Intro to mapping/permutations | Slow on dense 10-letter cases | O(n) |
| 2 Column-wise Carry | Simulate grade-school addition with carry | High (rejects early column mismatches) | After grasping Method 1 | Typically milliseconds | O(n + depth) |
| Two-Addend Walkthrough | Didactic specialization of Method 2 | Same as Method 2 (simpler state) | First exposure / classroom | Very fast | O(n) |