Skip to content

[Hacker Rank] Interview Preparation Kit: Dictionaries and Hashmaps: S… #292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package ae.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* SherlockAndAnagrams.
*
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams.md]]
* @link Solution notes [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/sherlock_and_anagrams-solution-notes.md]]
*/
public class SherlockAndAnagrams {
private SherlockAndAnagrams() {}

/**
* factorial().
*/
public static BigInteger factorial(int number) {
BigInteger result = BigInteger.ONE;
for (int i = 1; i <= number; i++) {
result = result.multiply(new BigInteger(Integer.toString(i)));
}

return result;
}

/**
* sherlockAndAnagrams.
*/
public static int sherlockAndAnagrams(String sWord) {

Map<String, List<String>> candidates = new HashMap<>();
int size = sWord.length();

for (int i = 0; i < size; i++) {
for (int j = 0; j < size - i; j++) {
String substr = sWord.substring(i, size - j);

// Add substrings to a candidate list.
// two strings are anagrams if sorted strings are the same.
String anagramCandidate = Arrays.stream(substr.split(""))
.sorted()
.collect(Collectors.joining());


// Append candidates to dictionary by "sorted string" key
if (candidates.containsKey(anagramCandidate)) {
candidates.get(anagramCandidate).add(substr);
} else {
ArrayList<String> anagrams = new ArrayList<>();
anagrams.add(substr);
candidates.put(anagramCandidate, anagrams);
}
}
}

int total = 0;

// Final Anagram list
for (Map.Entry<String, List<String>> entry : candidates.entrySet()) {
int quantityOfAnagrams = entry.getValue().size();
int k = 2;

if (quantityOfAnagrams > 1) {
// Binomial coefficient: https://en.wikipedia.org/wiki/Binomial_coefficient
int count = factorial(quantityOfAnagrams).divide(
(factorial(k).multiply(factorial(quantityOfAnagrams - k)))
).intValue();
total += count;
}
}

return total;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package ae.hackerrank.interview_preparation_kit.dictionaries_and_hashmaps;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import util.JsonLoader;

/**
* SherlockAndAnagrams.
*
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/two-strings.md]]
*/
@TestInstance(Lifecycle.PER_CLASS)
class SherlockAndAnagramsTest {

/**
* SherlockAndAnagramsTestCase.
*/
public static class SherlockAndAnagramsTestCase {
/**
* SherlockAndAnagramsTestCase.TestCase.
*/
public static class TestCase {
public String input;
public Integer expected;
}

public String title;
public List<TestCase> tests;
}

List<SherlockAndAnagramsTestCase> testCases;

/**
* Sets up the test environment by loading test cases from a JSON file.
* The JSON file is located in the specified path relative to the project structure.
*
* @throws IOException if an error occurs while reading the JSON file.
*/
@BeforeAll
void setup() throws IOException {
String path = String.join("/", "hackerrank",
"interview_preparation_kit",
"dictionaries_and_hashmaps",
"sherlock_and_anagrams.testcases.json");

this.testCases = JsonLoader.loadJson(path, SherlockAndAnagramsTestCase.class);
}

private SherlockAndAnagramsTest() {}

/**
* sherlockAndAnagrams.
*/
@Test void sherlockAndAnagrams() {
for (SherlockAndAnagramsTestCase _testCases : this.testCases) {

for (SherlockAndAnagramsTestCase.TestCase test : _testCases.tests) {
Integer solutionFound = SherlockAndAnagrams.sherlockAndAnagrams(test.input);

assertEquals(test.expected, solutionFound,
"%s(%s) answer must be: %s".formatted(
"SherlockAndAnagrams.sherlockAndAnagrams",
test.input,
test.expected
)
);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# [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:

<https://en.wikipedia.org/wiki/Binomial_coefficient>

But the entire cost of this formula falls on the "factorial" function.

In javascript, the factorial quickly reaches results that return large numbers,
in scientific notation, losing precision.
This loss of precision can result in an erroneous result
in the final calculation of permutations.

To avoid this problem, it is necessary to introduce large number handling using BigInt.
Original file line number Diff line number Diff line change
@@ -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.