# Solution For Verbal Arithmetic Puzzle

At the time of writing this answer, there are 4 Verbal Arithmetic Puzzle problems on LeetCode:

- Word Ladder II (https://leetcode.com/problems/word-ladder-ii/)
- Verbal Arithmetic Puzzle (https://leetcode.com/problems/verbal-arithmetic-puzzle/)
- Find Words That Can Be Formed by Characters (https://leetcode.com/problems/find-words-that-can-be-formed-by-characters/)
- Word Search II (https://leetcode.com/problems/word-search-ii/)

In this answer, we will focus on the Verbal Arithmetic Puzzle problem.

## Problem Statement

Given an equation like “SEND + MORE = MONEY”, where each letter represents a unique digit from 0 to 9, and the equation is guaranteed to have a valid solution, find the digits that each letter represents.

## Example:

Input: “SEND + MORE = MONEY”

Output:

{

“S”: 9,

“E”: 5,

“N”: 6,

“D”: 7,

“M”: 1,

“O”: 0,

“R”: 8,

“Y”: 2

}

## Solution Approach

The problem can be solved using a recursive backtracking algorithm. We start by creating a mapping of each letter to a digit, and then try to solve the equation.

For example, if we have the equation “SEND + MORE = MONEY”, we can create a mapping like {S: *, E: *, N: *, D: *, M: *, O: *, R: *, Y: *}.

Next, we try to fill in the numbers for each letter, one by one. We start with the first letter, and try all possible digits from 0 to 9. For each digit, we check if the equation is valid so far. If it is, we recursively try to fill in the next letter. If we reach the end of the equation and all digits have been filled, we have found a solution.

If at any point, we find that the equation is not valid, we backtrack to the previous letter and try a different digit. This is the recursive backtracking algorithm.

One important thing to note is that we have to keep track of the carries while adding two numbers. For example, consider the case where S=1 and E=2 in the equation “SEND + MORE = MONEY”. When we add S+E, we get a carry of 1. So, when we add N+R+1, we have to account for this carry. Similarly, when we add E+O, we have to check if there is a carry from the previous addition.

## Code Implementation

The following is a Python implementation of the recursive backtracking algorithm:

“`

class Solution:

def is_valid(self, mapping, s1, s2, s3):

n1 = self.get_number(mapping, s1)

n2 = self.get_number(mapping, s2)

n3 = self.get_number(mapping, s3)

return n1 + n2 == n3

```
def get_number(self, mapping, s):
res = 0
for c in s:
res = res * 10 + mapping[c]
return res
def solve(self, idx, carry, mapping, s1, s2, s3):
if idx == len(s3):
return carry == 0
c1 = s1[-1 - idx] if idx < len(s1) else ''
c2 = s2[-1 - idx] if idx < len(s2) else ''
c3 = s3[-1 - idx]
if c3 in mapping:
if mapping[c3] != carry:
return False
return self.solve(idx+1, 0, mapping, s1, s2, s3)
for d in range(10):
if str(d) in mapping.values():
continue
if d == 0 and (c1 == '' or c2 == '' or c3 == ''):
continue
n1 = mapping.get(c1, 0)
n2 = mapping.get(c2, 0)
s = n1 + n2 + carry
if s % 10 != d:
continue
mapping[c3] = d
if self.solve(idx+1, s//10, mapping, s1, s2, s3):
return True
del mapping[c3]
return False
def solvePuzzle(self, words: List[str], result: str) -> Dict[str, int]:
mapping = {}
s1, s2, s3 = words[0], words[1], result
if len(s3) > len(s1) + len(s2):
return {}
s1, s2 = s1.zfill(len(s3)), s2.zfill(len(s3))
if self.solve(0, 0, mapping, s1, s2, s3):
return mapping
return {}
```

“`

The `is_valid()`

function checks if the equation is valid so far. The `get_number()`

function converts a string to the corresponding integer based on the current mapping.

The `solve()`

function is the recursive backtracking algorithm. It takes as input the current index (i.e., the position of the letter/digit that we are trying to fill), the current carry, the current mapping, and the three strings s1, s2, s3. If we have filled all letters, and the equation is valid, we return True. Otherwise, we try to fill the current letter using all possible digits. If we find a valid mapping, we recursively try to fill the next letter. If we reach the end of the equation and all digits have been filled, we have found a solution.

The `solvePuzzle()`

function takes as input the two lists of strings representing the two words and the result, and returns a dictionary mapping each letter to the corresponding digit. It first creates the initial mapping, and then calls the `solve()`

function to find the solution.

## Complexity Analysis

The worst-case time complexity of the algorithm is O(10^n), where n is the number of unique letters in the equation. This is because we have to try all possible digits for each letter. However, in practice, the algorithm is much faster because we can prune the search tree by checking if the equation is valid at each point.

The worst-case space complexity of the algorithm is also O(n), where n is the number of unique letters in the equation. This is because we have to store the mapping for each letter. However, in practice, the space complexity is much lower because we can reuse the same mapping for each recursive call.

## Conclusion

The Verbal Arithmetic Puzzle problem can be solved using a recursive backtracking algorithm that tries all possible combinations of digits for each letter. The key idea is to use the is_valid() function to check if the equation is valid at each point, and backtrack whenever we encounter an invalid state. The algorithm has a worst-case time complexity of O(10^n) and a worst-case space complexity of O(n), where n is the number of unique letters in the equation.

## Step by Step Implementation For Verbal Arithmetic Puzzle

This problem can be solved using a recursive backtracking algorithm. We start by assigning each letter a digit from 0 to 9. Then, we check if the resulting equation is valid. If it is, we return the solution. If not, we backtrack and try a different assignment for the letter. Here is the pseudocode for the algorithm: def solve(letters, equation): # base case: all letters have been assigned if len(letters) == 0: # check if equation is valid if check(equation): return equation else: return None # recursive case: try all possible assignments for the first letter for i in range(10): # assign the letter to the digit i letters[0] = i # solve the subproblem with the remaining letters solution = solve(letters[1:], equation) # if a solution was found, return it if solution != None: return solution # no solution was found return None def check(equation): # TODO: check if equation is valid return True

def isSolvable(self, words: List[str], result: str) -> bool: def backtrack(i,carry,used): if i == len(words[0]): return carry == 0 for d in range(10): if used[d]: continue used[d] = True if backtrack(i+1,carry+d*sum(w[i]-'A'+1 for w in words),used): return True used[d] = False used = [False]*10 return backtrack(0,0,used)

// Given an equation like: // SEND // + MORE // -------- // MONEY // We want to determine what digits go in each slot such that the equation is correct. // In this case, we would return: // { // S: 9, // E: 5, // N: 6, // D: 7, // M: 1, // O: 0, // R: 8, // Y: 2 // } // This is a backtracking problem. We can start by assigning the first letter // (in this case, "S") a digit, then recursively checking if the remaining letters // can be assigned valid digits such that the equation remains correct. // If at any point we find that there are no valid digits remaining for a letter, // we backtrack and try a different digit for the previous letter. function verbalArithmeticPuzzle(equation) { let letters = {}; let firstLetters = []; // We first need to get all the unique letters in the equation // and keep track of which letters are the first letters in each word. for (let i = 0; i < equation.length; i++) { for (let j = 0; j < equation[i].length; j++) { const letter = equation[i][j]; if (!letters[letter]) { letters[letter] = true; if (j === 0) firstLetters.push(letter); } } } // We'll use this array to keep track of which digits have been used. // For example, if we've used the digit "9" for the letter "S", then // digitUsed[9] will be true. const digitUsed = new Array(10).fill(false); // We can start by assigning the first letter of the first word a digit, // then recursively checking if the remaining letters can be assigned valid digits. for (let d = 0; d <= 9; d++) { // If this digit has already been used, we can skip it. if (digitUsed[d]) continue; // We'll keep track of which letters have been assigned digits in this object. // For example, if we've assigned the digit "9" for the letter "S", then // assigned[S] will be 9. const assigned = {}; assigned[firstLetters[0]] = d; // We can mark this digit as used since we'll be assigning it to the first letter. digitUsed[d] = true; // If all the letters have been assigned digits and the equation is still correct, // then we have found a valid solution and can return it. if (solve(1, assigned, digitUsed, equation)) { return assigned; } // If we reach this point, it means that the equation was not correct // even after assigning the digit "d" to the first letter, so we'll try another digit. // We'll also need to unmark this digit as used since we didn't actually use it. digitUsed[d] = false; } // If we reach this point, it means we couldn't find a valid solution. return null; } // This is the recursive function that checks if the remaining letters can be // assigned digits such that the equation remains correct. // // "index" represents which letter we are currently assigning a digit to. // "assigned" is an object that keeps track of which letters have been assigned digits. // "digitUsed" is an array that keeps track of which digits have been used. function solve(index, assigned, digitUsed, equation) { // If we have assigned digits to all the letters, we need to check if the equation is still correct. if (index === equation.length) { return isValid(assigned, equation); } // We'll keep track of which letters are the first letters in each word. const firstLetters = []; for (let i = 0; i < equation.length; i++) { firstLetters.push(equation[i][0]); } // We can start by assigning the first letter of the current word a digit, // then recursively checking if the remaining letters can be assigned valid digits. for (let d = 0; d <= 9; d++) { // If this digit has already been used, we can skip it. if (digitUsed[d]) continue; // We'll keep track of which letters have been assigned digits in this object. // For example, if we've assigned the digit "9" for the letter "S", then // assigned[S] will be 9. const newAssigned = { ...assigned }; newAssigned[firstLetters[index]] = d; // We can mark this digit as used since we'll be assigning it to the current letter. const newDigitUsed = digitUsed.slice(); newDigitUsed[d] = true; // If all the letters have been assigned digits and the equation is still correct, // then we have found a valid solution and can return it. if (solve(index + 1, newAssigned, newDigitUsed, equation)) { return newAssigned; } } // If we reach this point, it means that the equation was not correct // even after trying all possible digits for the current letter, so we'll backtrack. return false; } // This function checks if the equation is still correct after assigning all the letters digits. function isValid(assigned, equation) { // We'll keep track of the sum of each word in the equation. // For example, if we have assigned the digits {S: 9, E: 5, N: 6, D: 7} to the letters // in the word "SEND", then the sum of that word would be 956. const sum = []; // We need to loop through each word in the equation and calculate the sum of the digits. for (let i = 0; i < equation.length; i++) { sum.push(0); for (let j = 0; j < equation[i].length; j++) { const letter = equation[i][j]; sum[i] = sum[i] * 10 + assigned[letter]; } } // We can check if the equation is correct by comparing the sums of each word. // For example, if we have the equation "SEND + MORE = MONEY", then the sums of each word would be: // // sum[0] = SEND => 956 // sum[1] = MORE => 567 // sum[2] = MONEY => 1023 // // We can check if the equation is correct by comparing the first sum to the sum of the remaining sums. // In this case, we would have 956 === 567 + 1023, which is correct. for (let i = 1; i < sum.length; i++) { sum[0] -= sum[i]; } return sum[0] === 0; }

The problem is as follows: Given an equation with numbers and words, determine if the equation is solvable and if so, what the solution is. The equation will be in the form "NUM1 + NUM2 = WORDSUM". Words can be added together to form new words (e.g. "TWO" + "TWO" = "FOUR"), and each letter has a value associated with it: A = 1 B = 2 ... Z = 26 For example, given the equation "TWO + TWO = FOUR", we can see that this is solvable because "FOUR" is the sum of the values of the letters in "TWO" and "TWO". The solution to this equation would be "A = 1, B = 2". Given the equation "SEND + MORE = MONEY", we can see that this is also solvable: S = 19 E = 5 N = 14 D = 4 M = 13 O = 15 R = 18 Y = 25 The solution to this equation would be "A = 9, B = 5, C = 6, D = 7, E = 8". However, given the equation "THIS + IS + IRREAL = REALITY", we can see that this is not solvable because there is no combination of values that would sum to the value of "REALITY".

using System; using System.Collections.Generic; public class Solution { // This is a verbal arithmetic puzzle solver. // Given a set of words and a sum, // it will find a unique assignment of digits to the letters // such that the sum is correct and all the words are valid. public void SolvePuzzle(string[] words, string sum) { // A map from letters to the possible digits they could represent Dictionary> letterMap = new Dictionary >(); // A map from letters to the number of times they appear in the sum Dictionary sumLetterCounts = new Dictionary (); // A map from letters to the number of times they appear in the words Dictionary wordLetterCounts = new Dictionary (); // Add all the letters in the sum to the maps foreach (char c in sum) { if (!letterMap.ContainsKey(c)) { letterMap.Add(c, new HashSet ()); } if (!sumLetterCounts.ContainsKey(c)) { sumLetterCounts.Add(c, 0); } sumLetterCounts[c]++; } // Add all the letters in the words to the maps foreach (string word in words) { foreach (char c in word) { if (!letterMap.ContainsKey(c)) { letterMap.Add(c, new HashSet ()); } if (!wordLetterCounts.ContainsKey(c)) { wordLetterCounts.Add(c, 0); } wordLetterCounts[c]++; } } // We will use a depth-first search to find a solution // We need to keep track of the letters we have assigned so far // as well as thecarry value for each position in the sum Dictionary assignment = new Dictionary (); int[] carry = new int[sum.Length]; if (SolvePuzzle(letterMap, sumLetterCounts, wordLetterCounts, assignment, carry, 0)) { Console.WriteLine("Solution found:"); // Print out the assignment foreach (KeyValuePair kvp in assignment) { Console.WriteLine(kvp.Key + "=" + kvp.Value); } } else { Console.WriteLine("No solution found"); } } // This is the recursive function for the depth-first search private bool SolvePuzzle(Dictionary > letterMap, Dictionary sumLetterCounts, Dictionary wordLetterCounts, Dictionary assignment, int[] carry, int position) { // If we have assigned all the letters, then check if the sum is correct if (position == sum.Length) { // Check if the carry values are all 0 foreach (int c in carry) { if (c != 0) { return false; } } // Check if the words are all valid foreach (string word in words) { int wordValue = 0; foreach (char c in word) { wordValue *= 10; wordValue += assignment[c]; } if (wordValue == 0) { return false; } } return true; } // This function assigns a digit to a letter // and then recurses to the next letter private bool AssignDigit(Dictionary > letterMap, Dictionary sumLetterCounts, Dictionary wordLetterCounts, Dictionary assignment, int[] carry, int position, char letter) { // We only want to try digits that are not already in use HashSet usedDigits = new HashSet (); // Add all the digits that have already been assigned to other letters foreach (KeyValuePair kvp in assignment) { usedDigits.Add(kvp.Value); } // We also need to make sure that the digit we choose // does not violate the constraints imposed by the sum // For example, if the sum is "SEND+MORE=MONEY" // and we have already assigned "S=9" and "E=5", // then we know that "N+R=Y" so the only possible digits for "N" and "R" are {0,2,4,6,8}. if (position < sum.Length - 1) { int sumValue = 0; foreach (char c in assignment.Keys) { sumValue *= 10; sumValue += assignment[c]; } int targetValue = (carry[position] + sumValue) % 10; // If the letter appears in the sum, // then we need to make sure the digit we choose // is consistent with the target value if (sumLetterCounts[letter] > 0) { // If the target value is 0 and the letter appears in the sum, // then the letter must be the first letter in the sum. // This is because if the letter appears later in the sum, // then its contribution would be multiplied by 10 // and the target value would not be 0. if (targetValue == 0 && position > 0) { return false; } // We only want to try digits that are less than the target value for (int d = 0; d < targetValue; d++) { if (!usedDigits.Contains(d)) { letterMap[letter].Add(d); } } // If the target value is not 0, // then we also want to try the digit that is one greater than the target value // (e.g. if the target value is 5, then we want to try the digit 6 as well) if (targetValue < 9 && !usedDigits.Contains(targetValue + 1)) { letterMap[letter].Add(targetValue + 1); } } // If the letter does not appear in the sum, // then we only want to try digits that are less than 10 else { for (int d = 0; d < 10; d++) { if (!usedDigits.Contains(d)) { letterMap[letter].Add(d); } } } // Recurse through all the possible digits for the letter foreach (char d in letterMap[letter]) { // Assign the digit to the letter assignment[letter] = d; // If the letter appears in the sum, // then we need to update the carry values if (sumLetterCounts[letter] > 0) { int sumValue = 0; foreach (char c in assignment.Keys) { sumValue *= 10; sumValue += assignment[c]; } int targetValue = (carry[position] + sumValue) % 10; if (d == targetValue) { // If the digit is the same as the target value, // then we don't need to carry over any value if (SolvePuzzle(letterMap, sumLetterCounts, wordLetterCounts, assignment, carry, position + 1)) { return true; } } else if (d == targetValue + 1) { // If the digit is one greater than the target value, // then we need to carry over a value of 1 carry[position + 1] = 1; if (SolvePuzzle(letterMap, sumLetterCounts, wordLetterCounts, assignment, carry, position + 1)) { return true; } carry[position + 1] = 0; } } // If the letter does not appear in the sum, // then we don't need to update the carry values else { if (SolvePuzzle(letterMap, sumLetterCounts, wordLetterCounts, assignment, carry, position + 1)) { return true; } } // Remove the digit from the letter's possible values letterMap[letter].Remove(d); } return false; } }