From e59c1ee8a64d3309ad07117193a19006c57fb11f Mon Sep 17 00:00:00 2001 From: Gonzalo Diaz Date: Sun, 28 Jul 2024 00:10:26 -0400 Subject: [PATCH 1/3] =?UTF-8?q?[Hacker=20Rank]=20Interview=20Preparation?= =?UTF-8?q?=20Kit:=20Dictionaries=20and=20Hashmaps:=20Sherlock=20and=20Ana?= =?UTF-8?q?grams.=20Solved=20=E2=9C=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sherlock_and_anagrams.md | 114 ++++++++++++++++++ .../sherlock_and_anagrams.test.ts | 58 +++++++++ .../sherlock_and_anagrams.ts | 52 ++++++++ 3 files changed, 224 insertions(+) create mode 100644 docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md create mode 100644 src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.test.ts create mode 100644 src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts diff --git a/docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md b/docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md new file mode 100644 index 00000000..dd9b1bd2 --- /dev/null +++ b/docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md @@ -0,0 +1,114 @@ +# [Sherlock and Anagrams](https://www.hackerrank.com/challenges/sherlock-and-anagrams) + +- Difficulty: `#medium` +- Category: `#ProblemSolvingMedium` `#DictionariesAndHashmaps` `#Strings` + +Two strings are [http://en.wikipedia.org/wiki/Anagram](anagrams) of each other +if the letters of one string can be rearranged to form the other string. +Given a string, find the number of pairs of substrings of the string that are +anagrams of each other. + +## Example + +`s = mom` + +The list of all anagrammatic pairs is `[m, m]`, `[mo, om]` +at positions `[[0], [2]]`, `[[0, 1], [1, 2]]` respectively. + +## Function Description + +Complete the function sherlockAndAnagrams in the editor below. + +*sherlockAndAnagrams* has the following parameter(s): + +- `string s`: a string + +## Returns + +- `int`: the number of unordered anagrammatic pairs of substrings in **`s`** + +## Input Format + +The first line contains an integer `q`, the number of queries. +Each of the next `q` lines contains a string `s` to analyze. + +## Constraints + +- $ 1 \leq 10 \leq 10 $ +- $ 2 \leq $ lenght of `s` $ \leq 100 $ + +`s` contains only lowercase letters in the range ascii[a-z]. + +## Sample Input 0 + +```text +2 +abba +abcd +``` + +## Sample Output 0 + +```text +4 +0 +``` + +## Explanation 0 + +The list of all anagrammatic pairs is `[a, a]`, `[ab, ba]`, +`[b, b]` and `[abb, bba]` at positions `[[0], [3]]`, `[[0, 1]], [[2, 3]]`, +`[[1], [2]]` and `[[0, 1, 2], [1, 2, 3]]` respectively. + +No anagrammatic pairs exist in the second query as no character repeats. + +## Sample Input 1 + +```text +2 +ifailuhkqq +kkkk +```` + +## Sample Output 1 + +```text +3 +10 +``` + +## Explanation 1 + +For the first query, we have anagram pairs `[i, i]`, `[q, q]` +and `[ifa, fai]` at positions `[[0], [3]]`, `[[8], [9]]` +and `[[0, 1, 2], [1, 2, 3]]` respectively. + +For the second query: + +There are `6` anagrams of the form `[k, k]` at positions `[[0, 1]]`, + `[[0], [2]]`, `[[0], [3]]`, `[[1], [2]]`, `[[1], [3]]` and `[[2], [3]]`. + +There are 3 anagrams of the form `[kk, kk]` at positions `[[0, 1], [1, 2]]`, +`[[0, 1], [2, 3]]` and `[[1, 2], [2, 3]]`. + +There is 1 anagram of the form `[kkk, kkk]` at position `[[0, 1, 2], [1, 2, 3]]`. + +## Sample Input 2 + +```text +1 +cdcd +``` + +## Sample Output 2 + +```text +5 +``` + +## Explanation 2 + +There are two anagrammatic pairs of length `1`: `[c, c]` and `[d, d]`. +There are three anagrammatic pairs of length `2`: +`[cd, dc]`, `[cd, cd]`, `[dc, cd]` at positions +`[[0, 1] [1, 2]]`, `[[0, 1], [2, 3]]`, `[1, 2], [2, 3]` respectively. diff --git a/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.test.ts b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.test.ts new file mode 100644 index 00000000..ac1be576 --- /dev/null +++ b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from '@jest/globals'; +import { logger as console } from '../../../logger'; + +import { sherlockAndAnagrams } from './sherlock_and_anagrams'; + +const TEST_CASES = [ + { + title: 'Sample Test Case 0', + tests: [ + { + input: 'abba', + expected: 4 + }, + { + input: 'abcd', + expected: 0 + } + ] + }, + { + title: 'Sample Test Case 1', + tests: [ + { + input: 'ifailuhkqq', + expected: 3 + }, + { + input: 'kkkk', + expected: 10 + } + ] + }, + { + title: 'Sample Test Case 1', + tests: [ + { + input: 'cdcd', + expected: 5 + } + ] + } +]; + +describe('sherlock_and_anagrams', () => { + it('sherlockAndAnagrams test cases', () => { + expect.assertions(5); + + TEST_CASES.forEach((testSet) => { + testSet.tests.forEach((test) => { + const answer = sherlockAndAnagrams(test.input); + + console.debug(`checkMagazine(${test.input}) solution found: ${answer}`); + + expect(answer).toStrictEqual(test.expected); + }); + }); + }); +}); diff --git a/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts new file mode 100644 index 00000000..6ae7ff8f --- /dev/null +++ b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts @@ -0,0 +1,52 @@ +/** + * @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md]] + */ + +function factorial(n: number): number { + if (n == 0) { + return 1; + } + return n * factorial(n - 1); +} + +export function sherlockAndAnagrams(s: string): number { + const candidates: Record = {}; + const size = s.length; + + for (let i = 0; i < size; i++) { + for (let j = 0; j < size - i; j++) { + const substr = s.substring(i, size - j); + + // Add substrings to a candidate list. + // two strings are anagrams if sorted strings are the same. + + const anagram_candidate = substr.split('').sort().join(''); + if (anagram_candidate in candidates) { + candidates[anagram_candidate].push(substr); + } else { + candidates[anagram_candidate] = [substr]; + } + } + } + + let count = 0; + // Final Anagram list + for (const i of Object.keys(candidates)) { + const total = candidates[i].length; + const k = 2; + + if (total <= 1) { + delete candidates[i]; + } else { + // Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient + count += Math.floor( + factorial(total) / (factorial(k) * factorial(total - k)) + ); + } + } + console.debug(`filtered candidates: ${count}`); + + return count; +} + +export default { sherlockAndAnagrams }; From 09517b890f2e7b3f04d7ed7c9b330e4d262f9822 Mon Sep 17 00:00:00 2001 From: Gonzalo Diaz Date: Sun, 28 Jul 2024 00:16:47 -0400 Subject: [PATCH 2/3] [Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: Sherlock and Anagrams. Sonarcloud warning fixed: Provide a compare function that depends on "String.localeCompare", to reliably sort elements alphabetically. --- .../dictionaries_and_hashmaps/sherlock_and_anagrams.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts index 6ae7ff8f..8fa40c70 100644 --- a/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts +++ b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts @@ -20,7 +20,10 @@ export function sherlockAndAnagrams(s: string): number { // Add substrings to a candidate list. // two strings are anagrams if sorted strings are the same. - const anagram_candidate = substr.split('').sort().join(''); + const anagram_candidate = substr + .split('') + .sort((a: string, b: string) => a.localeCompare(b)) + .join(''); if (anagram_candidate in candidates) { candidates[anagram_candidate].push(substr); } else { From 43df46ca36dd02c059f6db8b0d8243b64dbf8f34 Mon Sep 17 00:00:00 2001 From: Gonzalo Diaz Date: Sun, 28 Jul 2024 13:03:17 -0400 Subject: [PATCH 3/3] [Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: Sherlock and Anagrams. Clean Code: better variable naming. --- .../sherlock_and_anagrams.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts index 8fa40c70..2ab97cbe 100644 --- a/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts +++ b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts @@ -34,16 +34,17 @@ export function sherlockAndAnagrams(s: string): number { let count = 0; // Final Anagram list - for (const i of Object.keys(candidates)) { - const total = candidates[i].length; + for (const word of Object.keys(candidates)) { + const quantity_of_anagrams = candidates[word].length; const k = 2; - if (total <= 1) { - delete candidates[i]; + if (quantity_of_anagrams <= 1) { + delete candidates[word]; } else { // Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient count += Math.floor( - factorial(total) / (factorial(k) * factorial(total - k)) + factorial(quantity_of_anagrams) / + (factorial(k) * factorial(quantity_of_anagrams - k)) ); } }