Skip to content

Commit de2912d

Browse files
author
Gonzalo Diaz
committed
[Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: Sherlock and Anagrams. Solved ✅.
1 parent b199ebf commit de2912d

File tree

5 files changed

+345
-0
lines changed

5 files changed

+345
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# [Sherlock and Anagrams](https://www.hackerrank.com/challenges/sherlock-and-anagrams)
2+
3+
- Difficulty: `#medium`
4+
- Category: `#ProblemSolvingMedium` `#DictionariesAndHashmaps` `#Strings`
5+
6+
## About solution
7+
8+
To answer the question of "how many pairs" of words can be anagrammed
9+
using fragments from adjacent letters of an initial word, two steps are needed:
10+
11+
1) Obtain all possible fragment candidates to be anagrams,
12+
from each of the possible fragments that can be generated
13+
from adjacent letters of a word.
14+
15+
2) For each list of candidate anagrams,
16+
calculate all possible permutations and add them up.
17+
The total gives the answer.
18+
19+
The second part of this problem can be solved with the binomial coefficient formula:
20+
21+
<https://en.wikipedia.org/wiki/Binomial_coefficient>
22+
23+
But the entire cost of this formula falls on the "factorial" function.
24+
25+
The factorial quickly reaches results that return large numbers,
26+
overflowing the length of primitive number types.
27+
28+
To avoid this problem in C#, it is necessary to introduce large number handling
29+
using System.Numerics.BigInteger
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# [Sherlock and Anagrams](https://www.hackerrank.com/challenges/sherlock-and-anagrams)
2+
3+
- Difficulty: `#medium`
4+
- Category: `#ProblemSolvingMedium` `#DictionariesAndHashmaps` `#Strings`
5+
6+
Two strings are [http://en.wikipedia.org/wiki/Anagram](anagrams) of each other
7+
if the letters of one string can be rearranged to form the other string.
8+
Given a string, find the number of pairs of substrings of the string that are
9+
anagrams of each other.
10+
11+
## Example
12+
13+
`s = mom`
14+
15+
The list of all anagrammatic pairs is `[m, m]`, `[mo, om]`
16+
at positions `[[0], [2]]`, `[[0, 1], [1, 2]]` respectively.
17+
18+
## Function Description
19+
20+
Complete the function sherlockAndAnagrams in the editor below.
21+
22+
*sherlockAndAnagrams* has the following parameter(s):
23+
24+
- `string s`: a string
25+
26+
## Returns
27+
28+
- `int`: the number of unordered anagrammatic pairs of substrings in **`s`**
29+
30+
## Input Format
31+
32+
The first line contains an integer `q`, the number of queries.
33+
Each of the next `q` lines contains a string `s` to analyze.
34+
35+
## Constraints
36+
37+
- $ 1 \leq 10 \leq 10 $
38+
- $ 2 \leq $ lenght of `s` $ \leq 100 $
39+
40+
`s` contains only lowercase letters in the range ascii[a-z].
41+
42+
## Sample Input 0
43+
44+
```text
45+
2
46+
abba
47+
abcd
48+
```
49+
50+
## Sample Output 0
51+
52+
```text
53+
4
54+
0
55+
```
56+
57+
## Explanation 0
58+
59+
The list of all anagrammatic pairs is `[a, a]`, `[ab, ba]`,
60+
`[b, b]` and `[abb, bba]` at positions `[[0], [3]]`, `[[0, 1]], [[2, 3]]`,
61+
`[[1], [2]]` and `[[0, 1, 2], [1, 2, 3]]` respectively.
62+
63+
No anagrammatic pairs exist in the second query as no character repeats.
64+
65+
## Sample Input 1
66+
67+
```text
68+
2
69+
ifailuhkqq
70+
kkkk
71+
````
72+
73+
## Sample Output 1
74+
75+
```text
76+
3
77+
10
78+
```
79+
80+
## Explanation 1
81+
82+
For the first query, we have anagram pairs `[i, i]`, `[q, q]`
83+
and `[ifa, fai]` at positions `[[0], [3]]`, `[[8], [9]]`
84+
and `[[0, 1, 2], [1, 2, 3]]` respectively.
85+
86+
For the second query:
87+
88+
There are `6` anagrams of the form `[k, k]` at positions `[[0, 1]]`,
89+
`[[0], [2]]`, `[[0], [3]]`, `[[1], [2]]`, `[[1], [3]]` and `[[2], [3]]`.
90+
91+
There are 3 anagrams of the form `[kk, kk]` at positions `[[0, 1], [1, 2]]`,
92+
`[[0, 1], [2, 3]]` and `[[1, 2], [2, 3]]`.
93+
94+
There is 1 anagram of the form `[kkk, kkk]` at position `[[0, 1, 2], [1, 2, 3]]`.
95+
96+
## Sample Input 2
97+
98+
```text
99+
1
100+
cdcd
101+
```
102+
103+
## Sample Output 2
104+
105+
```text
106+
5
107+
```
108+
109+
## Explanation 2
110+
111+
There are two anagrammatic pairs of length `1`: `[c, c]` and `[d, d]`.
112+
There are three anagrammatic pairs of length `2`:
113+
`[cd, dc]`, `[cd, cd]`, `[dc, cd]` at positions
114+
`[[0, 1] [1, 2]]`, `[[0, 1], [2, 3]]`, `[1, 2], [2, 3]` respectively.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/ctci-ransom-note.md]]
2+
3+
namespace algorithm_exercises_csharp.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps;
4+
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Numerics;
7+
8+
public class SherlockAndAnagrams
9+
{
10+
[ExcludeFromCodeCoverage]
11+
protected SherlockAndAnagrams() { }
12+
13+
/**
14+
* factorial().
15+
*/
16+
public static BigInteger factorial(int number)
17+
{
18+
BigInteger result = BigInteger.One;
19+
for (int i = 1; i <= number; i++)
20+
{
21+
result *= i;
22+
}
23+
24+
return result;
25+
}
26+
27+
public static int sherlockAndAnagrams(string s)
28+
{
29+
Dictionary<string, List<string>> candidates = [];
30+
31+
int size = s.Length;
32+
for (int i = 0; i < size; i++)
33+
{
34+
for (int j = 0; j < size - i; j++)
35+
{
36+
string word = s[i..(size - j)];
37+
char[] wordArray = word.ToCharArray();
38+
Array.Sort(wordArray);
39+
string anagramCandidate = new(wordArray);
40+
41+
if (candidates.TryGetValue(anagramCandidate, out List<string>? value))
42+
{
43+
value.Add(word);
44+
}
45+
else
46+
{
47+
candidates[anagramCandidate] = [word];
48+
}
49+
}
50+
}
51+
52+
int k = 2;
53+
54+
int total = candidates
55+
.Select(x => x.Value.Count)
56+
.Where(count => count > 1)
57+
.Sum(quantityOfAnagrams =>
58+
// Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient
59+
(int)(factorial(quantityOfAnagrams) / factorial(k) / factorial(quantityOfAnagrams - k)));
60+
61+
return total;
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
[
2+
{
3+
"title": "Sample Test Case 0",
4+
"tests": [
5+
{
6+
"input": "abba",
7+
"expected": 4
8+
},
9+
{
10+
"input": "abcd",
11+
"expected": 0
12+
}
13+
]
14+
},
15+
{
16+
"title": "Sample Test Case 1",
17+
"tests": [
18+
{
19+
"input": "ifailuhkqq",
20+
"expected": 3
21+
},
22+
{
23+
"input": "kkkk",
24+
"expected": 10
25+
}
26+
]
27+
},
28+
{
29+
"title": "Sample Test Case 1",
30+
"tests": [
31+
{
32+
"input": "cdcd",
33+
"expected": 5
34+
}
35+
]
36+
},
37+
{
38+
"title": "Test case 3",
39+
"tests": [
40+
{
41+
"input":
42+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
43+
"expected": 166650
44+
},
45+
{
46+
"input":
47+
"bbcaadacaacbdddcdbddaddabcccdaaadcadcbddadababdaaabcccdcdaacadcababbabbdbacabbdcbbbbbddacdbbcdddbaaa",
48+
"expected": 4832
49+
},
50+
{
51+
"input":
52+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
53+
"expected": 166650
54+
},
55+
{
56+
"input":
57+
"cacccbbcaaccbaacbbbcaaaababcacbbababbaacabccccaaaacbcababcbaaaaaacbacbccabcabbaaacabccbabccabbabcbba",
58+
"expected": 13022
59+
},
60+
{
61+
"input":
62+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
63+
"expected": 166650
64+
},
65+
{
66+
"input":
67+
"bbcbacaabacacaaacbbcaabccacbaaaabbcaaaaaaaccaccabcacabbbbabbbbacaaccbabbccccaacccccabcabaacaabbcbaca",
68+
"expected": 9644
69+
},
70+
{
71+
"input":
72+
"cbaacdbaadbabbdbbaabddbdabbbccbdaccdbbdacdcabdbacbcadbbbbacbdabddcaccbbacbcadcdcabaabdbaacdccbbabbbc",
73+
"expected": 6346
74+
},
75+
{
76+
"input":
77+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
78+
"expected": 166650
79+
},
80+
{
81+
"input":
82+
"babacaccaaabaaaaaaaccaaaccaaccabcbbbabccbbabababccaabcccacccaaabaccbccccbaacbcaacbcaaaaaaabacbcbbbcc",
83+
"expected": 8640
84+
},
85+
{
86+
"input":
87+
"bcbabbaccacbacaacbbaccbcbccbaaaabbbcaccaacaccbabcbabccacbaabbaaaabbbcbbbbbaababacacbcaabbcbcbcabbaba",
88+
"expected": 11577
89+
}
90+
]
91+
}
92+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace algorithm_exercises_csharp_test.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps;
2+
using algorithm_exercises_csharp_test.lib;
3+
using algorithm_exercises_csharp.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps;
4+
5+
[TestClass]
6+
public class SherlockAndAnagramsTest
7+
{
8+
public class SherlockAndAnagramsTestCase
9+
{
10+
/**
11+
* SherlockAndAnagramsTestCase.TestCase.
12+
*/
13+
public class TestCase
14+
{
15+
public string input { get; set; } = default!;
16+
public int expected { get; set; } = default!;
17+
}
18+
19+
public string title { get; set; } = default!;
20+
public List<TestCase> tests { get; set; } = default!;
21+
}
22+
23+
private List<SherlockAndAnagramsTestCase> testCases { get; set; } = default!;
24+
25+
[TestInitialize]
26+
public void testInitialize()
27+
{
28+
testCases = JsonLoader.resourceLoad<List<SherlockAndAnagramsTestCase>>(
29+
"hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.testcases.json"
30+
) ?? [];
31+
}
32+
33+
[TestMethod]
34+
public void testSherlockAndAnagrams()
35+
{
36+
int result;
37+
38+
foreach (SherlockAndAnagramsTestCase testSet in testCases)
39+
{
40+
foreach (SherlockAndAnagramsTestCase.TestCase test in testSet.tests)
41+
{
42+
result = SherlockAndAnagrams.sherlockAndAnagrams(test.input);
43+
Assert.AreEqual(test.expected, result);
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)