From de2912dfaa4d6a0c954c1f0863b826ba067540d8 Mon Sep 17 00:00:00 2001 From: Gonzalo Diaz Date: Sat, 29 Mar 2025 00:49:45 -0300 Subject: [PATCH] =?UTF-8?q?[Hacker=20Rank]=20Interview=20Preparation=20Kit?= =?UTF-8?q?:=20Dictionaries=20and=20Hashmaps:=20Sherlock=20and=20Anagrams.?= =?UTF-8?q?=20Solved=20=E2=9C=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sherlock_and_anagrams-solution-notes.md | 29 +++++ .../sherlock_and_anagrams.md | 114 ++++++++++++++++++ .../SherlockAndAnagrams.cs | 63 ++++++++++ .../sherlock_and_anagrams.testcases.json | 92 ++++++++++++++ .../SherlockAndAnagrams.Test.cs | 47 ++++++++ 5 files changed, 345 insertions(+) create mode 100644 docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams-solution-notes.md create mode 100644 docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md create mode 100644 src/algorithm_exercises_csharp/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.cs create mode 100644 src/algorithm_exercises_csharp_test/Resources/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.testcases.json create mode 100644 src/algorithm_exercises_csharp_test/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.Test.cs diff --git a/docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams-solution-notes.md b/docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams-solution-notes.md new file mode 100644 index 0000000..2e34468 --- /dev/null +++ b/docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams-solution-notes.md @@ -0,0 +1,29 @@ +# [Sherlock and Anagrams](https://www.hackerrank.com/challenges/sherlock-and-anagrams) + +- Difficulty: `#medium` +- Category: `#ProblemSolvingMedium` `#DictionariesAndHashmaps` `#Strings` + +## About solution + +To answer the question of "how many pairs" of words can be anagrammed +using fragments from adjacent letters of an initial word, two steps are needed: + +1) Obtain all possible fragment candidates to be anagrams, + from each of the possible fragments that can be generated + from adjacent letters of a word. + +2) For each list of candidate anagrams, + calculate all possible permutations and add them up. + The total gives the answer. + +The second part of this problem can be solved with the binomial coefficient formula: + + + +But the entire cost of this formula falls on the "factorial" function. + +The factorial quickly reaches results that return large numbers, +overflowing the length of primitive number types. + +To avoid this problem in C#, it is necessary to introduce large number handling +using System.Numerics.BigInteger 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 0000000..dd9b1bd --- /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/algorithm_exercises_csharp/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.cs b/src/algorithm_exercises_csharp/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.cs new file mode 100644 index 0000000..536b4eb --- /dev/null +++ b/src/algorithm_exercises_csharp/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.cs @@ -0,0 +1,63 @@ +// @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/ctci-ransom-note.md]] + +namespace algorithm_exercises_csharp.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps; + +using System.Diagnostics.CodeAnalysis; +using System.Numerics; + +public class SherlockAndAnagrams +{ + [ExcludeFromCodeCoverage] + protected SherlockAndAnagrams() { } + + /** + * factorial(). + */ + public static BigInteger factorial(int number) + { + BigInteger result = BigInteger.One; + for (int i = 1; i <= number; i++) + { + result *= i; + } + + return result; + } + + public static int sherlockAndAnagrams(string s) + { + Dictionary> candidates = []; + + int size = s.Length; + for (int i = 0; i < size; i++) + { + for (int j = 0; j < size - i; j++) + { + string word = s[i..(size - j)]; + char[] wordArray = word.ToCharArray(); + Array.Sort(wordArray); + string anagramCandidate = new(wordArray); + + if (candidates.TryGetValue(anagramCandidate, out List? value)) + { + value.Add(word); + } + else + { + candidates[anagramCandidate] = [word]; + } + } + } + + int k = 2; + + int total = candidates + .Select(x => x.Value.Count) + .Where(count => count > 1) + .Sum(quantityOfAnagrams => + // Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient + (int)(factorial(quantityOfAnagrams) / factorial(k) / factorial(quantityOfAnagrams - k))); + + return total; + } +} diff --git a/src/algorithm_exercises_csharp_test/Resources/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.testcases.json b/src/algorithm_exercises_csharp_test/Resources/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.testcases.json new file mode 100644 index 0000000..f0e7c3f --- /dev/null +++ b/src/algorithm_exercises_csharp_test/Resources/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.testcases.json @@ -0,0 +1,92 @@ +[ + { + "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 + } + ] + }, + { + "title": "Test case 3", + "tests": [ + { + "input": + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": 166650 + }, + { + "input": + "bbcaadacaacbdddcdbddaddabcccdaaadcadcbddadababdaaabcccdcdaacadcababbabbdbacabbdcbbbbbddacdbbcdddbaaa", + "expected": 4832 + }, + { + "input": + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": 166650 + }, + { + "input": + "cacccbbcaaccbaacbbbcaaaababcacbbababbaacabccccaaaacbcababcbaaaaaacbacbccabcabbaaacabccbabccabbabcbba", + "expected": 13022 + }, + { + "input": + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": 166650 + }, + { + "input": + "bbcbacaabacacaaacbbcaabccacbaaaabbcaaaaaaaccaccabcacabbbbabbbbacaaccbabbccccaacccccabcabaacaabbcbaca", + "expected": 9644 + }, + { + "input": + "cbaacdbaadbabbdbbaabddbdabbbccbdaccdbbdacdcabdbacbcadbbbbacbdabddcaccbbacbcadcdcabaabdbaacdccbbabbbc", + "expected": 6346 + }, + { + "input": + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected": 166650 + }, + { + "input": + "babacaccaaabaaaaaaaccaaaccaaccabcbbbabccbbabababccaabcccacccaaabaccbccccbaacbcaacbcaaaaaaabacbcbbbcc", + "expected": 8640 + }, + { + "input": + "bcbabbaccacbacaacbbaccbcbccbaaaabbbcaccaacaccbabcbabccacbaabbaaaabbbcbbbbbaababacacbcaabbcbcbcabbaba", + "expected": 11577 + } + ] + } +] diff --git a/src/algorithm_exercises_csharp_test/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.Test.cs b/src/algorithm_exercises_csharp_test/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.Test.cs new file mode 100644 index 0000000..9d6a9a4 --- /dev/null +++ b/src/algorithm_exercises_csharp_test/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/SherlockAndAnagrams.Test.cs @@ -0,0 +1,47 @@ +namespace algorithm_exercises_csharp_test.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps; +using algorithm_exercises_csharp_test.lib; +using algorithm_exercises_csharp.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps; + +[TestClass] +public class SherlockAndAnagramsTest +{ + public class SherlockAndAnagramsTestCase + { + /** + * SherlockAndAnagramsTestCase.TestCase. + */ + public class TestCase + { + public string input { get; set; } = default!; + public int expected { get; set; } = default!; + } + + public string title { get; set; } = default!; + public List tests { get; set; } = default!; + } + + private List testCases { get; set; } = default!; + + [TestInitialize] + public void testInitialize() + { + testCases = JsonLoader.resourceLoad>( + "hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.testcases.json" + ) ?? []; + } + + [TestMethod] + public void testSherlockAndAnagrams() + { + int result; + + foreach (SherlockAndAnagramsTestCase testSet in testCases) + { + foreach (SherlockAndAnagramsTestCase.TestCase test in testSet.tests) + { + result = SherlockAndAnagrams.sherlockAndAnagrams(test.input); + Assert.AreEqual(test.expected, result); + } + } + } +}