|
4 | 4 | * @return {boolean}
|
5 | 5 | */
|
6 | 6 | const isSolvable = function (words, result) {
|
7 |
| - const firstChars = new Set() |
8 |
| - |
9 |
| - // this will hold the key as the character and multiple as the value |
10 |
| - const map = {} |
11 |
| - for (let i = 0; i < result.length; i++) { |
12 |
| - const char = result[i] |
13 |
| - if (!i) firstChars.add(char) |
14 |
| - if (!map.hasOwnProperty(char)) map[char] = 0 |
15 |
| - map[char] -= 10 ** (result.length - i - 1) |
16 |
| - } |
17 |
| - for (let j = 0; j < words.length; j++) { |
18 |
| - const word = words[j] |
19 |
| - for (let i = 0; i < word.length; i++) { |
20 |
| - const char = word[i] |
21 |
| - if (!i) firstChars.add(char) |
22 |
| - if (!map.hasOwnProperty(char)) map[char] = 0 |
23 |
| - map[char] += 10 ** (word.length - i - 1) |
| 7 | + const _isSolvable = (wordIndex, charIndex, wordsSum, resultSum, num) => { |
| 8 | + if (wordIndex >= words.length) { |
| 9 | + return wordsSum === resultSum |
24 | 10 | }
|
25 |
| - } |
26 |
| - |
27 |
| - const positives = [] |
28 |
| - const negatives = [] |
29 |
| - Object.entries(map).forEach((entry) => { |
30 |
| - if (entry[1] < 0) negatives.push(entry) |
31 |
| - else positives.push(entry) |
32 |
| - }) |
33 |
| - |
34 |
| - const numsUsed = new Set() |
35 |
| - const backtrack = (val = 0) => { |
36 |
| - // if we have used all the characters and the value is 0 the input is solvable |
37 |
| - if (!positives.length && !negatives.length) return val === 0 |
38 |
| - |
39 |
| - // get the store that we are going to examine depending on the value |
40 |
| - const store = |
41 |
| - val > 0 || (val === 0 && negatives.length) ? negatives : positives |
42 |
| - if (store.length === 0) return false |
43 |
| - const entry = store.pop() |
44 |
| - const [char, multiple] = entry |
45 |
| - |
46 |
| - // try every possible value watching out for the edge case that it was a first character |
47 |
| - for (let i = firstChars.has(char) ? 1 : 0; i < 10; i++) { |
48 |
| - if (numsUsed.has(i)) continue |
49 |
| - numsUsed.add(i) |
50 |
| - if (backtrack(i * multiple + val)) return true |
51 |
| - numsUsed.delete(i) |
| 11 | + const wordLen = words[wordIndex].length |
| 12 | + if (charIndex >= wordLen) { |
| 13 | + if (wordIndex === words.length - 1) { |
| 14 | + return _isSolvable(wordIndex + 1, 0, wordsSum, num, 0) |
| 15 | + } |
| 16 | + return _isSolvable(wordIndex + 1, 0, wordsSum + num, resultSum, 0) |
| 17 | + } |
| 18 | + const char = words[wordIndex][charIndex] |
| 19 | + if (map.get(char) !== undefined) { |
| 20 | + if (map.get(char) === 0 && num === 0 && charIndex >= 1) { |
| 21 | + return false |
| 22 | + } |
| 23 | + return _isSolvable( |
| 24 | + wordIndex, |
| 25 | + charIndex + 1, |
| 26 | + wordsSum, |
| 27 | + resultSum, |
| 28 | + num * 10 + map.get(char) |
| 29 | + ) |
| 30 | + } |
| 31 | + for (let digit = 0; digit <= 9; digit++) { |
| 32 | + if (digit === 0 && num === 0 && wordLen > 1) continue |
| 33 | + if (map.get(digit) !== undefined) continue |
| 34 | + map.set(digit, char) |
| 35 | + map.set(char, digit) |
| 36 | + if ( |
| 37 | + _isSolvable( |
| 38 | + wordIndex, |
| 39 | + charIndex + 1, |
| 40 | + wordsSum, |
| 41 | + resultSum, |
| 42 | + num * 10 + digit |
| 43 | + ) |
| 44 | + ) { |
| 45 | + return true |
| 46 | + } |
| 47 | + map.set(digit, undefined) |
| 48 | + map.set(char, undefined) |
52 | 49 | }
|
53 |
| - store.push(entry) |
54 | 50 | return false
|
55 | 51 | }
|
56 |
| - return backtrack() |
| 52 | + const map = new Map() |
| 53 | + words = [...words, result] |
| 54 | + return _isSolvable(0, 0, 0, 0, 0) |
57 | 55 | }
|
0 commit comments