Skip to content

Commit 93014ab

Browse files
authored
Merge pull request #330 from sir-gon/feature/sherlock_and_anagrams
[Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: S…
2 parents e424099 + ed2cae7 commit 93014ab

File tree

4 files changed

+299
-0
lines changed

4 files changed

+299
-0
lines changed
Lines changed: 114 additions & 0 deletions
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` `#dictionaries` `#hashmaps` `#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.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md]]
3+
*/
4+
5+
package hackerrank
6+
7+
import (
8+
"math/big"
9+
"slices"
10+
"strings"
11+
12+
"gon.cl/algorithms/utils/log"
13+
)
14+
15+
func factorialBig(n int64) *big.Int {
16+
if n <= 0 {
17+
return big.NewInt(1)
18+
}
19+
result := big.NewInt(1)
20+
for i := int64(2); i <= n; i++ {
21+
result.Mul(result, big.NewInt(i))
22+
}
23+
return result
24+
}
25+
26+
func sherlockAndAnagrams(s string) int32 {
27+
candidates := make(map[string][]string)
28+
size := len(s)
29+
30+
for i := 0; i < size; i++ {
31+
for j := 0; j < size-i; j++ {
32+
substr := s[i : size-j]
33+
34+
log.Debug("i: %d, size: %d, size - j: %d | substr: %s",
35+
i, size, size-j, substr)
36+
37+
anagramCandidateSlice := strings.Split(substr, "")
38+
slices.Sort(anagramCandidateSlice)
39+
anagramCandidate := strings.Join(anagramCandidateSlice, "")
40+
41+
_, ok := candidates[anagramCandidate]
42+
if ok {
43+
// Key exists
44+
candidates[anagramCandidate] = append(candidates[anagramCandidate], substr)
45+
} else {
46+
// Key does not exist
47+
candidates[anagramCandidate] = []string{substr}
48+
}
49+
}
50+
}
51+
52+
var total int32 = 0
53+
var qCandidates int32 = 0
54+
55+
// # Final Anagram list
56+
for word := range candidates {
57+
quantityOfAnagrams := int32(len(candidates[word]))
58+
k := int32(2)
59+
60+
if quantityOfAnagrams <= 1 {
61+
delete(candidates, word)
62+
} else {
63+
// # Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient
64+
qCandidates += quantityOfAnagrams
65+
66+
result := big.NewInt(1)
67+
68+
count := result.Div(
69+
factorialBig(int64(quantityOfAnagrams)),
70+
result.Mul(
71+
factorialBig(int64(k)),
72+
factorialBig(int64(quantityOfAnagrams-k))))
73+
74+
total += int32(count.Int64())
75+
76+
log.Debug("Partial anagrams of %s: %d", word, count)
77+
}
78+
79+
log.Debug(
80+
"sherlock_and_anagrams(%s) Filtered # candidates: %d", s, qCandidates)
81+
log.Debug("sherlock_and_anagrams(%s) # anagrams: %d", s, total)
82+
}
83+
return total
84+
}
85+
86+
func SherlockAndAnagrams(s string) int32 {
87+
return sherlockAndAnagrams((s))
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[
2+
{
3+
"title": "Test case 3",
4+
"tests": [
5+
{
6+
"input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
7+
"expected": 166650
8+
},
9+
{
10+
"input": "bbcaadacaacbdddcdbddaddabcccdaaadcadcbddadababdaaabcccdcdaacadcababbabbdbacabbdcbbbbbddacdbbcdddbaaa",
11+
"expected": 4832
12+
},
13+
{
14+
"input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
15+
"expected": 166650
16+
},
17+
{
18+
"input": "cacccbbcaaccbaacbbbcaaaababcacbbababbaacabccccaaaacbcababcbaaaaaacbacbccabcabbaaacabccbabccabbabcbba",
19+
"expected": 13022
20+
},
21+
{
22+
"input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
23+
"expected": 166650
24+
},
25+
{
26+
"input": "bbcbacaabacacaaacbbcaabccacbaaaabbcaaaaaaaccaccabcacabbbbabbbbacaaccbabbccccaacccccabcabaacaabbcbaca",
27+
"expected": 9644
28+
},
29+
{
30+
"input": "cbaacdbaadbabbdbbaabddbdabbbccbdaccdbbdacdcabdbacbcadbbbbacbdabddcaccbbacbcadcdcabaabdbaacdccbbabbbc",
31+
"expected": 6346
32+
},
33+
{
34+
"input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
35+
"expected": 166650
36+
},
37+
{
38+
"input": "babacaccaaabaaaaaaaccaaaccaaccabcbbbabccbbabababccaabcccacccaaabaccbccccbaacbcaacbcaaaaaaabacbcbbbcc",
39+
"expected": 8640
40+
},
41+
{
42+
"input": "bcbabbaccacbacaacbbaccbcbccbaaaabbbcaccaacaccbabcbabccacbaabbaaaabbbcbbbbbaababacacbcaabbcbcbcabbaba",
43+
"expected": 11577
44+
}
45+
]
46+
}
47+
]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package hackerrank
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"gon.cl/algorithms/utils"
10+
)
11+
12+
type SherlockAndAnagramsTest struct {
13+
Input string `json:"input"`
14+
Expected int32 `json:"expected"`
15+
}
16+
17+
type SherlockAndAnagramsTests struct {
18+
Title string `json:"title"`
19+
Tests []SherlockAndAnagramsTest `json:"tests"`
20+
}
21+
22+
var SherlockAndAnagramsTestCases []SherlockAndAnagramsTests
23+
24+
// You can use testing.T, if you want to test the code without benchmarking
25+
func SherlockAndAnagramsSetupSuite(t testing.TB) {
26+
wd, _ := os.Getwd()
27+
filepath := wd + "/sherlock-and-anagrams.testcases.json"
28+
t.Log("Setup test cases from JSON: ", filepath)
29+
30+
var _, err = utils.LoadJSON(filepath, &SherlockAndAnagramsTestCases)
31+
if err != nil {
32+
t.Log(err)
33+
}
34+
}
35+
36+
func TestSherlockAndAnagrams(t *testing.T) {
37+
38+
SherlockAndAnagramsSetupSuite(t)
39+
40+
for _, tt := range SherlockAndAnagramsTestCases {
41+
for _, testCase := range tt.Tests {
42+
testname := fmt.Sprintf("SherlockAndAnagrams(%s) => %d \n", testCase.Input, testCase.Expected)
43+
44+
t.Run(testname, func(t *testing.T) {
45+
ans := SherlockAndAnagrams(testCase.Input)
46+
assert.Equal(t, testCase.Expected, ans)
47+
})
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)