Tim Varley Logo
Tim Varley Principal AI Engineer and Tech Leader
Problem #98 hard

Anagramic squares

By replacing each of the letters in the word CARE with 11, 22, 99, and 66 respectively, we form a square number: 1296=3621296 = 36^2. What is remarkable is that, by using the same digital substitutions, the anagram, RACE, also forms a square number: 9216=9629216 = 96^2. We shall call CARE (and RACE) a square anagram word pair and specify further that leading zeroes are not permitted, neither may a different letter have the same digital value as another letter.

Using words.txt, a 16K text file containing nearly two-thousand common English words, find all the square anagram word pairs (a palindromic word is NOT considered to be an anagram of itself).

What is the largest square number formed by any member of such a pair?

NOTE: All anagrams formed must be contained in the given text file.

View on Project Euler

Implementations

cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
bool is_square(long long n) {
long long root = (long long)std::sqrt(n);
return root * root == n;
}
void try_mapping(const std::vector<char>& letter_list, int pos, std::vector<bool>& used, std::map<char, int>& mapping, const std::string& w1, const std::string& w2, long long& max_square) {
// Recursive backtracking to assign distinct digits 0-9 to letters, checking for valid square numbers
if (pos == letter_list.size()) {
if (mapping[w1[0]] == 0 || mapping[w2[0]] == 0) return;
long long num1 = 0, num2 = 0;
for (char c : w1) num1 = num1 * 10 + mapping[c];
for (char c : w2) num2 = num2 * 10 + mapping[c];
if (is_square(num1) && is_square(num2)) {
max_square = std::max({max_square, num1, num2});
}
return;
}
for (int d = 0; d < 10; ++d) {
if (used[d]) continue;
mapping[letter_list[pos]] = d;
used[d] = true;
try_mapping(letter_list, pos + 1, used, mapping, w1, w2, max_square);
used[d] = false;
}
}
long long anagramic_squares() {
std::ifstream file("src/p098_words.txt");
if (!file) return 0;
std::string content;
std::getline(file, content);
// parse words - split by comma and remove quotes
std::vector<std::string> words;
std::string word;
bool in_quote = false;
for (char c : content) {
if (c == '"') {
in_quote = !in_quote;
if (!in_quote && !word.empty()) {
words.push_back(word);
word.clear();
}
} else if (in_quote) {
word += c;
}
}
// group by sorted letters
std::map<std::string, std::vector<std::string>> anagrams;
for (const std::string& word : words) {
std::string sorted = word;
std::sort(sorted.begin(), sorted.end());
anagrams[sorted].push_back(word);
}
long long max_square = 0;
for (const auto& p : anagrams) {
if (p.second.size() < 2) continue;
for (size_t i = 0; i < p.second.size(); ++i) {
for (size_t j = i + 1; j < p.second.size(); ++j) {
std::string w1 = p.second[i], w2 = p.second[j];
// get unique letters
std::set<char> letters;
for (char c : w1) letters.insert(c);
for (char c : w2) letters.insert(c);
if (letters.size() > 10) continue; // impossible
// generate digit assignments using backtracking for efficiency
std::vector<char> letter_list(letters.begin(), letters.end());
std::vector<bool> used(10, false);
std::map<char, int> mapping;
try_mapping(letter_list, 0, used, mapping, w1, w2, max_square);
}
}
}
return max_square;
}

Solution Notes

Mathematical Background

The problem involves finding anagram pairs from a word list, then finding digit substitutions that make both words into perfect squares. This is a cryptarithm problem where letters represent digits 0-9 with unique assignments.

Algorithm Analysis

Group words by sorted letters to find anagrams. For each pair, use backtracking to try all possible digit assignments (checking for leading zeros), and validate if both resulting numbers are perfect squares. Track the maximum square found.

Performance

Varies by language: ~100ms in compiled languages, ~1s in Python due to backtracking complexity with word length and anagram group sizes.

Key Insights

  • Backtracking with pruning for invalid mappings (leading zeros, non-squares)
  • File parsing to extract word list from quoted, comma-separated format
  • Efficient anagram grouping using sorted strings as keys

Educational Value

Demonstrates constraint satisfaction problems, backtracking algorithms, and combinatorial search in programming.

Problem #98 is taken from Project Euler and licensed under CC BY-NC-SA 4.0.