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..2ab97cbe --- /dev/null +++ b/src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.ts @@ -0,0 +1,56 @@ +/** + * @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((a: string, b: string) => a.localeCompare(b)) + .join(''); + if (anagram_candidate in candidates) { + candidates[anagram_candidate].push(substr); + } else { + candidates[anagram_candidate] = [substr]; + } + } + } + + let count = 0; + // Final Anagram list + for (const word of Object.keys(candidates)) { + const quantity_of_anagrams = candidates[word].length; + const k = 2; + + if (quantity_of_anagrams <= 1) { + delete candidates[word]; + } else { + // Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient + count += Math.floor( + factorial(quantity_of_anagrams) / + (factorial(k) * factorial(quantity_of_anagrams - k)) + ); + } + } + console.debug(`filtered candidates: ${count}`); + + return count; +} + +export default { sherlockAndAnagrams };