From bcd060c6d37382f5e6f60a786da7e08474ec5dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A4ghib=20Hasan?= Date: Thu, 25 Apr 2024 16:43:16 +0300 Subject: [PATCH] add solution for sherlock and anagrams --- readme.md | 4 +- .../sherlock-and-anagrams.spec.ts | 21 +++++++ .../sherlock-and-anagrams.ts | 61 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.spec.ts create mode 100644 src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.ts diff --git a/readme.md b/readme.md index af9940c..12295ab 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -## Algorithms (16/ 167) +## Algorithms (17/ 167) | Name | Tags | Solution | | --------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------- | @@ -10,7 +10,7 @@ | Array Manipulation | `Arrays` | [TypeScript](./src/algorithms/arrays/array-manipulation) | | Ransom Note | `Hash Tables` | [TypeScript](./src/algorithms/hashtables/ransom-note) | | Two Strings | `Hash Tables` | [TypeScript](./src/algorithms/hashtables/two-strings) | -| Sherlock and Anagrams | `Hash Tables` | | +| Sherlock and Anagrams | `Hash Tables` | [TypeScript](./src/algorithms/hashtables/sherlock-and-anagrams) | | Count Triplets | `Hash Tables` | | | Frequency Queries | `Hash Tables` | | | Making Anagrams | `Strings` | [TypeScript](./src/algorithms/strings/make-anagram) | diff --git a/src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.spec.ts b/src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.spec.ts new file mode 100644 index 0000000..1e057c3 --- /dev/null +++ b/src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.spec.ts @@ -0,0 +1,21 @@ +import { + sherlockAndAnagrams, + sherlockAndAnagrams2, +} from './sherlock-and-anagrams' + +describe('Sherlock and Anagrams', () => { + it('should return correct number of anagram pairs for "mom"', () => { + expect(sherlockAndAnagrams('mom')).toBe(2) + expect(sherlockAndAnagrams2('mom')).toBe(2) + }) + + it('should return the correct number of anagram pairs for "abba"', () => { + expect(sherlockAndAnagrams('abba')).toBe(4) + expect(sherlockAndAnagrams2('abba')).toBe(4) + }) + + it('should return 0 for no anagrams', () => { + expect(sherlockAndAnagrams('abcd')).toBe(0) + expect(sherlockAndAnagrams2('abcd')).toBe(0) + }) +}) diff --git a/src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.ts b/src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.ts new file mode 100644 index 0000000..9fd7bae --- /dev/null +++ b/src/algorithms/hashtables/sherlock-and-anagrams/sherlock-and-anagrams.ts @@ -0,0 +1,61 @@ +/** + * Sherlock and Anagrams + * + * Two strings are 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 and 1 respectively. + * + * Constraints + * + * 1 <= q <= 100 at positions respectively. + * + */ + +export function sherlockAndAnagrams(s: string): number { + const substringCounts = new Map() + + for (let i = 0; i < s.length; i++) { + for (let j = i + 1; j <= s.length; j++) { + let curr = [...s.slice(i, j)].sort().join('') + + if (substringCounts.has(curr)) { + substringCounts.set(curr, substringCounts.get(curr)! + 1) + } else { + substringCounts.set(curr, 1) + } + } + } + + return [...substringCounts.values()].reduce( + (acc, curr) => acc + (curr * (curr - 1)) / 2, + 0 + ) +} + +export function sherlockAndAnagrams2(s: string): number { + const generateSortedSubstrings = (_: unknown, i: number) => + Array.from({ length: s.length - i }, (_, j) => + s + .slice(j, j + i + 1) + .split('') + .sort() + .join('') + ) + + const sortedSubstringCounts = s + .split('') + .flatMap(generateSortedSubstrings) + .reduce( + (map, substr) => map.set(substr, (map.get(substr) || 0) + 1), + new Map() + ) + + return [...sortedSubstringCounts.values()].reduce( + (acc, count) => acc + (count * (count - 1)) / 2, + 0 + ) +}