2021-05-05 17:13:49 +00:00
---
id: 5900f3cf1000cf542c50fee1
title: 'Problem 98: Anagramic squares'
challengeType: 5
forumTopicId: 302215
dashedName: problem-98-anagramic-squares
---
# --description--
2021-07-16 05:33:16 +00:00
By replacing each of the letters in the word CARE with 1, 2, 9, and 6 respectively, we form a square number: $1296 = 36^2$. What is remarkable is that, by using the same digital substitutions, the anagram, RACE, also forms a square number: $9216 = 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.
2021-05-05 17:13:49 +00:00
2021-07-16 05:33:16 +00:00
Using the `words` array, find all the square anagram word pairs (a palindromic word is NOT considered to be an anagram of itself).
2021-05-05 17:13:49 +00:00
What is the largest square number formed by any member of such a pair?
**Note:** All anagrams formed must be contained in the given `words` array.
# --hints--
2021-07-16 05:33:16 +00:00
`anagramicSquares(['CARE', 'RACE'])` should return a number.
2021-05-05 17:13:49 +00:00
```js
2021-07-16 05:33:16 +00:00
assert(typeof anagramicSquares(['CARE', 'RACE']) === 'number');
2021-05-05 17:13:49 +00:00
```
2021-07-16 05:33:16 +00:00
`anagramicSquares(['CARE', 'RACE'])` should return `9216` .
2021-05-05 17:13:49 +00:00
```js
2021-07-16 05:33:16 +00:00
assert.strictEqual(anagramicSquares(['CARE', 'RACE']), 9216);
```
`anagramicSquares(testWords1)` should return `4761` .
```js
assert.strictEqual(anagramicSquares(_testWords1), 4761);
```
`anagramicSquares(testWords2)` should return `18769` .
```js
assert.strictEqual(anagramicSquares(_testWords2), 18769);
2021-05-05 17:13:49 +00:00
```
# --seed--
2021-07-16 05:33:16 +00:00
## --after-user-code--
```js
const _testWords1 = [
"DAMAGE","DANGER","DANGEROUS","DARK","DATA","DATE","DAUGHTER","DAY","DEAD","DEAL","DEATH","DEBATE","DEBT","DECADE","DECIDE","DECISION","DECLARE","DEEP","DEFENCE","DEFENDANT","DEFINE","DEFINITION","DEGREE","DELIVER","DEMAND","DEMOCRATIC","DEMONSTRATE","DENY","DEPARTMENT","DEPEND","DEPUTY","DERIVE","DESCRIBE","DESCRIPTION","DESIGN","DESIRE","DESK","DESPITE","DESTROY","DETAIL","DETAILED","DETERMINE","DEVELOP","DEVELOPMENT","DEVICE","DIE","DIFFERENCE","DIFFERENT","DIFFICULT","DIFFICULTY","DINNER","DIRECT","DIRECTION","DIRECTLY","DIRECTOR","DISAPPEAR","DISCIPLINE","DISCOVER","DISCUSS","DISCUSSION","DISEASE","DISPLAY","DISTANCE","DISTINCTION","DISTRIBUTION","DISTRICT","DIVIDE","DIVISION","DO","DOCTOR","DOCUMENT","DOG","DOMESTIC","DOOR","DOUBLE","DOUBT","DOWN","DRAW","DRAWING","DREAM","DRESS","DRINK","DRIVE","DRIVER","DROP","DRUG","DRY","DUE","DURING","DUTY","LABOUR","LACK","LADY","LAND","LANGUAGE","LARGE","LARGELY","LAST","LATE","LATER","LATTER","LAUGH","LAUNCH","LAW","LAWYER","LAY","LEAD","LEADER","LEADERSHIP","LEADING","LEAF","LEAGUE","LEAN","LEARN","LEAST","LEAVE","LEFT","LEG","LEGAL","LEGISLATION","LENGTH","LESS","LET","LETTER","LEVEL","LIABILITY","LIBERAL","LIBRARY","LIE","LIFE","LIFT","LIGHT","LIKE","LIKELY","LIMIT","LIMITED","LINE","LINK","LIP","LIST","LISTEN","LITERATURE","LITTLE","LIVE","LIVING","LOAN","LOCAL","LOCATION","LONG","LOOK","LORD","LOSE","LOSS","LOT","LOVE","LOVELY","LOW","LUNCH"
];
const _testWords2 = [
"A","ABILITY","ABLE","ABOUT","ABOVE","ABSENCE","ABSOLUTELY","ACADEMIC","ACCEPT","ACCESS","ACCIDENT","ACCOMPANY","ACCORDING","ACCOUNT","ACHIEVE","ACHIEVEMENT","ACID","ACQUIRE","ACROSS","ACT","ACTION","ACTIVE","ACTIVITY","ACTUAL","ACTUALLY","ADD","ADDITION","ADDITIONAL","ADDRESS","ADMINISTRATION","ADMIT","ADOPT","ADULT","ADVANCE","ADVANTAGE","ADVICE","ADVISE","AFFAIR","AFFECT","AFFORD","AFRAID","AFTER","AFTERNOON","AFTERWARDS","AGAIN","AGAINST","AGE","AGENCY","AGENT","AGO","AGREE","AGREEMENT","AHEAD","AID","AIM","AIR","AIRCRAFT","ALL","ALLOW","ALMOST","ALONE","ALONG","ALREADY","ALRIGHT","ALSO","ALTERNATIVE","ALTHOUGH","ALWAYS","AMONG","AMONGST","AMOUNT","AN","ANALYSIS","ANCIENT","AND","ANIMAL","ANNOUNCE","ANNUAL","ANOTHER","ANSWER","ANY","ANYBODY","ANYONE","ANYTHING","ANYWAY","APART","APPARENT","APPARENTLY","APPEAL","APPEAR","APPEARANCE","APPLICATION","APPLY","APPOINT","APPOINTMENT","APPROACH","APPROPRIATE","APPROVE","AREA","ARGUE","ARGUMENT","ARISE","ARM","ARMY","AROUND","ARRANGE","ARRANGEMENT","ARRIVE","ART","ARTICLE","ARTIST","AS","ASK","ASPECT","ASSEMBLY","ASSESS","ASSESSMENT","ASSET","ASSOCIATE","ASSOCIATION","ASSUME","ASSUMPTION","AT","ATMOSPHERE","ATTACH","ATTACK","ATTEMPT","ATTEND","ATTENTION","ATTITUDE","ATTRACT","ATTRACTIVE","AUDIENCE","AUTHOR","AUTHORITY","AVAILABLE","AVERAGE","AVOID","AWARD","AWARE","AWAY","AYE","BABY","BACK","BACKGROUND","BAD","BAG","BALANCE","BALL","BAND","BANK","BAR","BASE","BASIC","BASIS","BATTLE","BE","BEAR","BEAT","BEAUTIFUL","BECAUSE","BECOME","BED","BEDROOM","BEFORE","BEGIN","BEGINNING","BEHAVIOUR","BEHIND","BELIEF","BELIEVE","BELONG","BELOW","BENEATH","BENEFIT","BESIDE","BEST","BETTER","BETWEEN","BEYOND","BIG","BILL","BIND","BIRD","BIRTH","BIT","BLACK","BLOCK","BLOOD","BLOODY","BLOW","BLUE","BOARD","BOAT","BODY","BONE","BOOK","BORDER","BOTH","BOTTLE","BOTTOM","BOX","BOY","BRAIN","BRANCH","BREAK","BREATH","BRIDGE","BRIEF","BRIGHT","BRING","BROAD","BROTHER","BUDGET","BUILD","BUILDING","BURN","BUS","BUSINESS","BUSY","BUT","BUY","BY","CABINET","CALL","CAMPAIGN","CAN","CANDIDATE","CAPABLE","CAPACITY","CAPITAL","CAR","CARD","CARE","CAREER","CAREFUL","CAREFULLY","CARRY","CASE","CASH","CAT","CATCH","CATEGORY","CAUSE","CELL","CENTRAL","CENTRE","CENTURY","CERTAIN","CERTAINLY","CHAIN","CHAIR","CHAIRMAN","CHALLENGE","CHANCE","CHANGE","CHANNEL","CHAPTER","CHARACTER","CHARACTERISTIC","CHARGE","CHEAP","CHECK","CHEMICAL","CHIEF","CHILD","CHOICE","CHOOSE","CHURCH","CIRCLE","CIRCUMSTANCE","CITIZEN","CITY","CIVIL","CLAIM","CLASS","CLEAN","CLEAR","CLEARLY","CLIENT","CLIMB","CLOSE","CLOSELY","CLOTHES","CLUB","COAL","CODE","COFFEE","COLD","COLLEAGUE","COLLECT","COLLECTION","COLLEGE","COLOUR","COMBINATION","COMBINE","COME","COMMENT","COMMERCIAL","COMMISSION","COMMIT","COMMITMENT","COMMITTEE","COMMON","COMMUNICATION","COMMUNITY","COMPANY","COMPARE","COMPARISON","COMPETITION","COMPLETE","COMPLETELY","COMPLEX","COMPONENT","COMPUTER","CONCENTRATE","CONCENTRATION","CONCEPT","CONCERN","CONCERNED","CONCLUDE","CONCLUSION","CONDITION","CONDUCT","CONFERENCE","CONFIDENCE","CONFIRM","CONFLICT","CONGRESS","CONNECT","CONNECTION","CONSEQUENCE","CONSERVATIVE","CONSIDER","CONSIDERABLE","CONSIDERATION","CONSIST","CONSTANT","CONSTRUCTION","CONSUMER","CONTACT","CONTAIN","CONTENT","CONTEXT","CONTINUE","CONTRACT","CONTRAST","CONTRIBUTE","CONTRIBUTION","CONTROL","CONVENTION","CONVERSATION","COPY","CORNER","CORPORATE","CORRECT","COS","COST","COULD","COUNCIL","COUNT","COUNTRY","COUNTY","COUPLE","COURSE","COURT","COVER","CREATE","CREATION","CREDIT","CRIME","CRIMINAL","CRISIS","CRITERION","CRITICAL","CRITICISM","CROSS","CROWD","CRY","CULTURAL","CULTURE","CUP","CURRENT","CURRENTLY","CURRICULUM","CUSTOMER","CUT","DAMAGE","DANGER","DANGEROUS","DARK","DATA","DATE","DAUGHTER","DAY","DEAD","DEAL","DEATH","DEBATE","DEBT","DECADE","DECIDE","DECISION","DECLARE","DEEP","DEFENCE","DEFENDANT","DEFINE","DEFINITION","DEGREE","DELIVER","DEMAND","DEMOCRATIC","DEMONSTRATE","DENY","DEPARTMENT","DEPEND","DEPUTY","DERIVE","DESCRIBE","DESCRIPTION","DESIGN","DESIRE","DESK","DESPITE","DESTROY","DETAIL","DETAILED","DETERMINE","DEVELOP","DEVELOPMENT","DEVI
];
```
2021-05-05 17:13:49 +00:00
## --seed-contents--
```js
2021-07-16 05:33:16 +00:00
function anagramicSquares(words) {
2021-05-05 17:13:49 +00:00
return true;
}
// Only change code above this line
2021-07-16 05:33:16 +00:00
const testWords1 = [
"DAMAGE","DANGER","DANGEROUS","DARK","DATA","DATE","DAUGHTER","DAY","DEAD","DEAL","DEATH","DEBATE","DEBT","DECADE","DECIDE","DECISION","DECLARE","DEEP","DEFENCE","DEFENDANT","DEFINE","DEFINITION","DEGREE","DELIVER","DEMAND","DEMOCRATIC","DEMONSTRATE","DENY","DEPARTMENT","DEPEND","DEPUTY","DERIVE","DESCRIBE","DESCRIPTION","DESIGN","DESIRE","DESK","DESPITE","DESTROY","DETAIL","DETAILED","DETERMINE","DEVELOP","DEVELOPMENT","DEVICE","DIE","DIFFERENCE","DIFFERENT","DIFFICULT","DIFFICULTY","DINNER","DIRECT","DIRECTION","DIRECTLY","DIRECTOR","DISAPPEAR","DISCIPLINE","DISCOVER","DISCUSS","DISCUSSION","DISEASE","DISPLAY","DISTANCE","DISTINCTION","DISTRIBUTION","DISTRICT","DIVIDE","DIVISION","DO","DOCTOR","DOCUMENT","DOG","DOMESTIC","DOOR","DOUBLE","DOUBT","DOWN","DRAW","DRAWING","DREAM","DRESS","DRINK","DRIVE","DRIVER","DROP","DRUG","DRY","DUE","DURING","DUTY","LABOUR","LACK","LADY","LAND","LANGUAGE","LARGE","LARGELY","LAST","LATE","LATER","LATTER","LAUGH","LAUNCH","LAW","LAWYER","LAY","LEAD","LEADER","LEADERSHIP","LEADING","LEAF","LEAGUE","LEAN","LEARN","LEAST","LEAVE","LEFT","LEG","LEGAL","LEGISLATION","LENGTH","LESS","LET","LETTER","LEVEL","LIABILITY","LIBERAL","LIBRARY","LIE","LIFE","LIFT","LIGHT","LIKE","LIKELY","LIMIT","LIMITED","LINE","LINK","LIP","LIST","LISTEN","LITERATURE","LITTLE","LIVE","LIVING","LOAN","LOCAL","LOCATION","LONG","LOOK","LORD","LOSE","LOSS","LOT","LOVE","LOVELY","LOW","LUNCH"
2021-05-05 17:13:49 +00:00
];
2021-07-16 05:33:16 +00:00
anagramicSquares(testWords1);
2021-05-05 17:13:49 +00:00
```
# --solutions--
```js
2021-07-16 05:33:16 +00:00
function anagramicSquares(words) {
// Based on https://www.mathblog.dk/project-euler-98-anagrams-square-numbers/
function findMaximumSquare(squares, word1, word2) {
let maximumSquare = 0;
for (let i = 0; i < squares.length ; i + + ) {
const length = squares[i].toString().length;
if (length < word1.length ) {
continue;
}
if (length > word1.length) {
break;
}
const word1Square = squares[i];
const letterToDigit = mapLettersToDigits(word1, word1Square);
const noProperMappingExist = Object.keys(letterToDigit).length === 0;
if (noProperMappingExist) {
continue;
}
const word2Square = getNumberFromMapping(word2, letterToDigit);
if (word2Square === 0) {
continue;
}
const doesWord2SquareExist = squares.indexOf(word2Square) !== -1;
if (doesWord2SquareExist) {
const pairMaximum = Math.max(word1Square, word2Square);
maximumSquare = Math.max(maximumSquare, pairMaximum);
}
}
return maximumSquare;
}
function getNumberFromMapping(word, letterToDigit) {
const wouldNumberHaveLeadingZero = letterToDigit[word[0]] === 0;
if (wouldNumberHaveLeadingZero) {
return 0;
}
let number = 0;
for (let i = 0; i < word.length ; i + + ) {
number = number * 10 + letterToDigit[word[i]];
}
return number;
}
function mapLettersToDigits(word, square) {
const letterToDigit = {};
for (let j = word.length - 1; j >= 0; j--) {
const curDigit = square % 10;
square = Math.floor(square / 10);
const curLetter = word[j];
const isLetterRepeated = letterToDigit.hasOwnProperty(curLetter);
if (isLetterRepeated) {
const isLetterUsedForTheSameDigit =
letterToDigit[curLetter] === curDigit;
if (isLetterUsedForTheSameDigit) {
continue;
}
return {};
}
const isDigitUsed = Object.values(letterToDigit).indexOf(curDigit) !== -1;
if (isDigitUsed) {
return {};
}
letterToDigit[curLetter] = curDigit;
}
return letterToDigit;
}
function groupWordsWithSameLetters(words) {
const lettersToWords = {};
for (let i = 0; i < words.length ; i + + ) {
const word = words[i];
const sortedLetters = word.split('').sort().join('');
if (!lettersToWords.hasOwnProperty(sortedLetters)) {
lettersToWords[sortedLetters] = [];
}
lettersToWords[sortedLetters].push(word);
}
return lettersToWords;
}
const lettersToWords = groupWordsWithSameLetters(words);
const anagrams = Object.keys(lettersToWords).filter(
letters => lettersToWords[letters].length > 1
);
const lengthOfLongestAnagram = anagrams
.map(anagram => anagram.length)
.sort((a, b) => b - a)[0];
const squares = [];
const numberLimit = (10 ** lengthOfLongestAnagram) ** 0.5;
for (let number = 2; number < numberLimit ; number + + ) {
const square = number ** 2;
squares.push(square);
}
let largestSquare = 0;
for (let i = 0; i < anagrams.length ; i + + ) {
const curWords = lettersToWords[anagrams[i]];
for (let j = 0; j < curWords.length ; j + + ) {
for (let k = j + 1; k < curWords.length ; k + + ) {
const squareValue = findMaximumSquare(
squares,
curWords[j],
curWords[k]
);
if (squareValue > largestSquare) {
largestSquare = squareValue;
}
}
}
}
return largestSquare;
}
2021-05-05 17:13:49 +00:00
```