Problem Statement: A cryptarithm is a mathematical puzzle where the goal is to find the correspondence between letters and digits such that the given arithmetic equation consisting of letters holds true.
Given a cryptarithm as an array of strings crypt, count the number of its valid solutions.
The solution is valid if each letter represents a different digit, and the leading digit of any multi-digit number is not zero.
crypt has the following structure: [word1, word2, word3], which stands for the word1 + word2 = word3 cryptarithm.
A cryptarithm is a type of mathematical puzzle where each letter represents a unique digit from 0 to 9. The challenge is to find a mapping that satisfies a specific arithmetic equation, usually an addition like Word1 + Word2 = Word3. There are two strict rules:
While brute-forcing all permutations works for k <= 10, a more advanced Backtracking approach can prune the search space by checking the addition column-by-column (from right to left). This allows you to discard invalid mappings as soon as a single column's sum (including the carry) fails to match.
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <cmath>
using namespace std;
// Helper to convert a string to a number based on current character mapping
long long stringToLong(const string& s, const vector<int>& mapping, const vector<char>& uniqueChars) {
long long num = 0;
for (char c : s) {
int digit = -1;
for (int i = 0; i < uniqueChars.size(); ++i) {
if (uniqueChars[i] == c) {
digit = mapping[i];
break;
}
}
num = num * 10 + digit;
}
return num;
}
int solveCryptarithm(vector<string> crypt) {
set<char> charSet;
for (const string& word : crypt) {
for (char c : word) charSet.insert(c);
}
// If there are more than 10 unique characters, no solution exists
if (charSet.size() > 10) return 0;
vector<char> uniqueChars(charSet.begin(), charSet.end());
int k = uniqueChars.size();
// Identify which characters are leading characters
vector<bool> isLeading(k, false);
for (const string& word : crypt) {
if (word.length() > 1) {
for (int i = 0; i < k; ++i) {
if (uniqueChars[i] == word[0]) {
isLeading[i] = true;
}
}
}
}
// Prepare digits 0-9 for permutation
vector<int> digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int solutionsCount = 0;
// We only need to permute the first 'k' digits out of 10.
// However, std::next_permutation works on the whole range.
// To optimize, we can use a recursive backtracking approach.
sort(digits.begin(), digits.end());
do {
bool leadingZero = false;
for (int i = 0; i < k; ++i) {
if (isLeading[i] && digits[i] == 0) {
leadingZero = true;
break;
}
}
if (!leadingZero) {
long long val1 = stringToLong(crypt[0], digits, uniqueChars);
long long val2 = stringToLong(crypt[1], digits, uniqueChars);
long long val3 = stringToLong(crypt[2], digits, uniqueChars);
if (val1 + val2 == val3) {
solutionsCount++;
}
}
// Advanced: std::next_permutation on 10 elements is 3.6 million iterations.
// To only permute the first K elements, we handle the tail.
reverse(digits.begin() + k, digits.end());
} while (next_permutation(digits.begin(), digits.end()));
return solutionsCount;
}
int main() {
// Example: SEND + MORE = MONEY
vector<string> crypt = {"SEND", "MORE", "MONEY"};
cout << "Number of solutions: " << solveCryptarithm(crypt) << endl;
return 0;
}