From 67cbd8e020e5ae0ea98c351e9547e2733d2c79a0 Mon Sep 17 00:00:00 2001 From: aladin002dz Date: Sun, 15 Oct 2023 16:23:24 +0100 Subject: [PATCH 1/7] Search: Rabin-Karp algorithm --- Search/RabinKarp.js | 49 +++++++++++++++++++++++++++++++++++ Search/test/RabinKarp.test.js | 31 ++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 Search/RabinKarp.js create mode 100644 Search/test/RabinKarp.test.js diff --git a/Search/RabinKarp.js b/Search/RabinKarp.js new file mode 100644 index 0000000000..cb509721d1 --- /dev/null +++ b/Search/RabinKarp.js @@ -0,0 +1,49 @@ +function hash(str, length) { + let hashValue = 0; + for (let i = 0; i < length; i++) { + hashValue = (hashValue * 256 + str.charCodeAt(i)) % 997; + } + return hashValue; +} + +function rabinKarpSearch(text, pattern) { + const BASE = 256; // The number of characters in the alphabet + const MOD = 997; // A prime number used for the hash function + const patternLength = pattern.length; + const textLength = text.length; + const hashPattern = hash(pattern, patternLength); + const hashText = []; + const indices = []; + + // Calculate the hash of the first substring in the text + hashText[0] = hash(text, patternLength); + + // Precompute BASE^(patternLength-1) % MOD + const basePow = Math.pow(BASE, patternLength - 1) % MOD; + + for (let i = 1; i <= textLength - patternLength + 1; i++) { + // Update the rolling hash by removing the first character + // and adding the next character in the text + hashText[i] = (BASE * (hashText[i - 1] - text.charCodeAt(i - 1) * basePow) + text.charCodeAt(i + patternLength - 1)) % MOD; + + // In case of hash collision, check character by character + if (hashText[i] < 0) { + hashText[i] += MOD; + } + + // Check if the hashes match and perform a character-wise comparison + if (hashText[i] === hashPattern) { + if (text.substring(i, i + patternLength) === pattern) { + indices.push(i); // Store the index where the pattern is found + } + } + } + + return indices.length > 0 ? indices : [-1]; // Return an array of indices where the pattern is found or [-1] if not found +} + +// Example usage: +const result = rabinKarpSearch("ABABDABACDABABCABAB", "DAB"); +console.log("Pattern found at indices:", result); + +export { rabinKarpSearch }; diff --git a/Search/test/RabinKarp.test.js b/Search/test/RabinKarp.test.js new file mode 100644 index 0000000000..a955060db7 --- /dev/null +++ b/Search/test/RabinKarp.test.js @@ -0,0 +1,31 @@ + +import { rabinKarpSearch } from "../RabinKarp"; + +describe("Rabin-Karp Search", function () { + it("should find the pattern in the text", function () { + const text = "ABABDABACDABABCABAB"; + const pattern = "DAB"; + const expected = [4, 9]; + + const result = rabinKarpSearch(text, pattern); + expect(result).to.deep.equal(expected); + }); + + it("should handle multiple occurrences of the pattern", function () { + const text = "ABABABABABAB"; + const pattern = "ABAB"; + const expected = [2, 4, 6, 8]; + + const result = rabinKarpSearch(text, pattern); + expect(result).to.deep.equal(expected); + }); + + it("should handle pattern not found", function () { + const text = "ABCD"; + const pattern = "XYZ"; + const expected = [-1]; + + const result = rabinKarpSearch(text, pattern); + expect(result).to.deep.equal(expected); + }); +}); From c2234258b3f3d827f2aab5d713cb2257b51cf6b4 Mon Sep 17 00:00:00 2001 From: aladin002dz Date: Sun, 15 Oct 2023 16:26:26 +0100 Subject: [PATCH 2/7] Prettier Style --- Bit-Manipulation/BinaryCountSetBits.js | 2 +- Maths/AutomorphicNumber.js | 2 +- Maths/test/AutomorphicNumber.test.js | 16 ++-- Search/InterpolationSearch.js | 2 +- Search/RabinKarp.js | 101 +++++++++++++------------ Search/test/RabinKarp.test.js | 61 ++++++++------- 6 files changed, 93 insertions(+), 91 deletions(-) diff --git a/Bit-Manipulation/BinaryCountSetBits.js b/Bit-Manipulation/BinaryCountSetBits.js index b879f3bd67..b959caf062 100644 --- a/Bit-Manipulation/BinaryCountSetBits.js +++ b/Bit-Manipulation/BinaryCountSetBits.js @@ -16,7 +16,7 @@ function BinaryCountSetBits(a) { let count = 0 while (a) { - a &= (a - 1) + a &= a - 1 count++ } diff --git a/Maths/AutomorphicNumber.js b/Maths/AutomorphicNumber.js index d1b6608316..ba008271fc 100644 --- a/Maths/AutomorphicNumber.js +++ b/Maths/AutomorphicNumber.js @@ -35,6 +35,6 @@ export const isAutomorphic = (n) => { n = Math.floor(n / 10) n_sq = Math.floor(n_sq / 10) } - + return true } diff --git a/Maths/test/AutomorphicNumber.test.js b/Maths/test/AutomorphicNumber.test.js index 19b963388c..57f40d27ee 100644 --- a/Maths/test/AutomorphicNumber.test.js +++ b/Maths/test/AutomorphicNumber.test.js @@ -9,19 +9,19 @@ describe('AutomorphicNumber', () => { }) test.each([ - { n: -3 , expected: false }, - { n: -25 , expected: false }, + { n: -3, expected: false }, + { n: -25, expected: false } ])('should return false when n is negetive', ({ n, expected }) => { expect(isAutomorphic(n)).toBe(false) }) test.each([ - { n: 7 , expected: false }, - { n: 83 , expected: false }, - { n: 0 , expected: true }, - { n: 1 , expected: true }, - { n: 376 , expected: true }, - { n: 90625 , expected: true }, + { n: 7, expected: false }, + { n: 83, expected: false }, + { n: 0, expected: true }, + { n: 1, expected: true }, + { n: 376, expected: true }, + { n: 90625, expected: true } ])('should return $expected when n is $n', ({ n, expected }) => { expect(isAutomorphic(n)).toBe(expected) }) diff --git a/Search/InterpolationSearch.js b/Search/InterpolationSearch.js index e6deae496f..93f3b78b0e 100644 --- a/Search/InterpolationSearch.js +++ b/Search/InterpolationSearch.js @@ -36,4 +36,4 @@ export function interpolationSearch(arr, key) { } return -1 -} \ No newline at end of file +} diff --git a/Search/RabinKarp.js b/Search/RabinKarp.js index cb509721d1..cb9a048ede 100644 --- a/Search/RabinKarp.js +++ b/Search/RabinKarp.js @@ -1,49 +1,52 @@ -function hash(str, length) { - let hashValue = 0; - for (let i = 0; i < length; i++) { - hashValue = (hashValue * 256 + str.charCodeAt(i)) % 997; - } - return hashValue; -} - -function rabinKarpSearch(text, pattern) { - const BASE = 256; // The number of characters in the alphabet - const MOD = 997; // A prime number used for the hash function - const patternLength = pattern.length; - const textLength = text.length; - const hashPattern = hash(pattern, patternLength); - const hashText = []; - const indices = []; - - // Calculate the hash of the first substring in the text - hashText[0] = hash(text, patternLength); - - // Precompute BASE^(patternLength-1) % MOD - const basePow = Math.pow(BASE, patternLength - 1) % MOD; - - for (let i = 1; i <= textLength - patternLength + 1; i++) { - // Update the rolling hash by removing the first character - // and adding the next character in the text - hashText[i] = (BASE * (hashText[i - 1] - text.charCodeAt(i - 1) * basePow) + text.charCodeAt(i + patternLength - 1)) % MOD; - - // In case of hash collision, check character by character - if (hashText[i] < 0) { - hashText[i] += MOD; - } - - // Check if the hashes match and perform a character-wise comparison - if (hashText[i] === hashPattern) { - if (text.substring(i, i + patternLength) === pattern) { - indices.push(i); // Store the index where the pattern is found - } - } - } - - return indices.length > 0 ? indices : [-1]; // Return an array of indices where the pattern is found or [-1] if not found -} - -// Example usage: -const result = rabinKarpSearch("ABABDABACDABABCABAB", "DAB"); -console.log("Pattern found at indices:", result); - -export { rabinKarpSearch }; +function hash(str, length) { + let hashValue = 0 + for (let i = 0; i < length; i++) { + hashValue = (hashValue * 256 + str.charCodeAt(i)) % 997 + } + return hashValue +} + +function rabinKarpSearch(text, pattern) { + const BASE = 256 // The number of characters in the alphabet + const MOD = 997 // A prime number used for the hash function + const patternLength = pattern.length + const textLength = text.length + const hashPattern = hash(pattern, patternLength) + const hashText = [] + const indices = [] + + // Calculate the hash of the first substring in the text + hashText[0] = hash(text, patternLength) + + // Precompute BASE^(patternLength-1) % MOD + const basePow = Math.pow(BASE, patternLength - 1) % MOD + + for (let i = 1; i <= textLength - patternLength + 1; i++) { + // Update the rolling hash by removing the first character + // and adding the next character in the text + hashText[i] = + (BASE * (hashText[i - 1] - text.charCodeAt(i - 1) * basePow) + + text.charCodeAt(i + patternLength - 1)) % + MOD + + // In case of hash collision, check character by character + if (hashText[i] < 0) { + hashText[i] += MOD + } + + // Check if the hashes match and perform a character-wise comparison + if (hashText[i] === hashPattern) { + if (text.substring(i, i + patternLength) === pattern) { + indices.push(i) // Store the index where the pattern is found + } + } + } + + return indices.length > 0 ? indices : [-1] // Return an array of indices where the pattern is found or [-1] if not found +} + +// Example usage: +const result = rabinKarpSearch('ABABDABACDABABCABAB', 'DAB') +console.log('Pattern found at indices:', result) + +export { rabinKarpSearch } diff --git a/Search/test/RabinKarp.test.js b/Search/test/RabinKarp.test.js index a955060db7..0f438253c9 100644 --- a/Search/test/RabinKarp.test.js +++ b/Search/test/RabinKarp.test.js @@ -1,31 +1,30 @@ - -import { rabinKarpSearch } from "../RabinKarp"; - -describe("Rabin-Karp Search", function () { - it("should find the pattern in the text", function () { - const text = "ABABDABACDABABCABAB"; - const pattern = "DAB"; - const expected = [4, 9]; - - const result = rabinKarpSearch(text, pattern); - expect(result).to.deep.equal(expected); - }); - - it("should handle multiple occurrences of the pattern", function () { - const text = "ABABABABABAB"; - const pattern = "ABAB"; - const expected = [2, 4, 6, 8]; - - const result = rabinKarpSearch(text, pattern); - expect(result).to.deep.equal(expected); - }); - - it("should handle pattern not found", function () { - const text = "ABCD"; - const pattern = "XYZ"; - const expected = [-1]; - - const result = rabinKarpSearch(text, pattern); - expect(result).to.deep.equal(expected); - }); -}); +import { rabinKarpSearch } from '../RabinKarp' + +describe('Rabin-Karp Search', function () { + it('should find the pattern in the text', function () { + const text = 'ABABDABACDABABCABAB' + const pattern = 'DAB' + const expected = [4, 9] + + const result = rabinKarpSearch(text, pattern) + expect(result).to.deep.equal(expected) + }) + + it('should handle multiple occurrences of the pattern', function () { + const text = 'ABABABABABAB' + const pattern = 'ABAB' + const expected = [2, 4, 6, 8] + + const result = rabinKarpSearch(text, pattern) + expect(result).to.deep.equal(expected) + }) + + it('should handle pattern not found', function () { + const text = 'ABCD' + const pattern = 'XYZ' + const expected = [-1] + + const result = rabinKarpSearch(text, pattern) + expect(result).to.deep.equal(expected) + }) +}) From 30821e0fc6ce76e294f2099f277337b9946a164a Mon Sep 17 00:00:00 2001 From: aladin002dz Date: Sun, 15 Oct 2023 16:52:41 +0100 Subject: [PATCH 3/7] Search: Rabin-Karp adding reference --- Search/RabinKarp.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Search/RabinKarp.js b/Search/RabinKarp.js index cb9a048ede..95d501b84f 100644 --- a/Search/RabinKarp.js +++ b/Search/RabinKarp.js @@ -1,3 +1,18 @@ +/* +* Implements the Rabin-Karp algorithm for pattern searching. +* +* The Rabin-Karp algorithm is a string searching algorithm that uses hashing to find patterns in strings. +* It is faster than naive string matching algorithms because it avoids comparing every character in the text. +* +* This implementation uses a rolling hash function to efficiently compute the hash values of substrings. +* It also uses a modulo operator to reduce the size of the hash values, which helps to prevent hash collisions. +* +* The algorithm returns an array of indices where the pattern is found in the text. If the pattern is not +* found, the algorithm returns an empty array. +* +* [Reference](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) +*/ + function hash(str, length) { let hashValue = 0 for (let i = 0; i < length; i++) { From a31f9ece757457b824779d98021c12188d9f63aa Mon Sep 17 00:00:00 2001 From: aladin002dz Date: Sun, 15 Oct 2023 16:55:59 +0100 Subject: [PATCH 4/7] Search: Rabin-Karp styling and remove unecessary logging --- Search/RabinKarp.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Search/RabinKarp.js b/Search/RabinKarp.js index 95d501b84f..5e4425b6bc 100644 --- a/Search/RabinKarp.js +++ b/Search/RabinKarp.js @@ -1,17 +1,17 @@ /* -* Implements the Rabin-Karp algorithm for pattern searching. -* -* The Rabin-Karp algorithm is a string searching algorithm that uses hashing to find patterns in strings. -* It is faster than naive string matching algorithms because it avoids comparing every character in the text. -* -* This implementation uses a rolling hash function to efficiently compute the hash values of substrings. -* It also uses a modulo operator to reduce the size of the hash values, which helps to prevent hash collisions. -* -* The algorithm returns an array of indices where the pattern is found in the text. If the pattern is not -* found, the algorithm returns an empty array. -* -* [Reference](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) -*/ + * Implements the Rabin-Karp algorithm for pattern searching. + * + * The Rabin-Karp algorithm is a string searching algorithm that uses hashing to find patterns in strings. + * It is faster than naive string matching algorithms because it avoids comparing every character in the text. + * + * This implementation uses a rolling hash function to efficiently compute the hash values of substrings. + * It also uses a modulo operator to reduce the size of the hash values, which helps to prevent hash collisions. + * + * The algorithm returns an array of indices where the pattern is found in the text. If the pattern is not + * found, the algorithm returns an empty array. + * + * [Reference](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) + */ function hash(str, length) { let hashValue = 0 @@ -60,8 +60,4 @@ function rabinKarpSearch(text, pattern) { return indices.length > 0 ? indices : [-1] // Return an array of indices where the pattern is found or [-1] if not found } -// Example usage: -const result = rabinKarpSearch('ABABDABACDABABCABAB', 'DAB') -console.log('Pattern found at indices:', result) - export { rabinKarpSearch } From 5d2706ad40daafbe49b141b0069e529411e6bd99 Mon Sep 17 00:00:00 2001 From: aladin002dz Date: Mon, 16 Oct 2023 09:05:55 +0100 Subject: [PATCH 5/7] Search: Rabin-Karp review notes --- Search/RabinKarp.js | 21 +++++++++++---------- Search/test/RabinKarp.test.js | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Search/RabinKarp.js b/Search/RabinKarp.js index 5e4425b6bc..046b47d7df 100644 --- a/Search/RabinKarp.js +++ b/Search/RabinKarp.js @@ -13,17 +13,10 @@ * [Reference](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) */ -function hash(str, length) { - let hashValue = 0 - for (let i = 0; i < length; i++) { - hashValue = (hashValue * 256 + str.charCodeAt(i)) % 997 - } - return hashValue -} +const BASE = 256 // The number of characters in the alphabet +const MOD = 997 // A prime number used for the hash function function rabinKarpSearch(text, pattern) { - const BASE = 256 // The number of characters in the alphabet - const MOD = 997 // A prime number used for the hash function const patternLength = pattern.length const textLength = text.length const hashPattern = hash(pattern, patternLength) @@ -57,7 +50,15 @@ function rabinKarpSearch(text, pattern) { } } - return indices.length > 0 ? indices : [-1] // Return an array of indices where the pattern is found or [-1] if not found + return indices.length > 0 ? indices : [] // Return an array of indices where the pattern is found or empty array if not found +} + +function hash(str, length) { + let hashValue = 0 + for (let i = 0; i < length; i++) { + hashValue = (hashValue * BASE + str.charCodeAt(i)) % MOD + } + return hashValue } export { rabinKarpSearch } diff --git a/Search/test/RabinKarp.test.js b/Search/test/RabinKarp.test.js index 0f438253c9..3d10097509 100644 --- a/Search/test/RabinKarp.test.js +++ b/Search/test/RabinKarp.test.js @@ -22,7 +22,7 @@ describe('Rabin-Karp Search', function () { it('should handle pattern not found', function () { const text = 'ABCD' const pattern = 'XYZ' - const expected = [-1] + const expected = [] const result = rabinKarpSearch(text, pattern) expect(result).to.deep.equal(expected) From 00726c33a867b708641dbabe4b454154e296ea68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sun, 29 Oct 2023 17:05:44 +0100 Subject: [PATCH 6/7] Simplify return --- Search/RabinKarp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Search/RabinKarp.js b/Search/RabinKarp.js index 046b47d7df..e6d6394ede 100644 --- a/Search/RabinKarp.js +++ b/Search/RabinKarp.js @@ -50,7 +50,7 @@ function rabinKarpSearch(text, pattern) { } } - return indices.length > 0 ? indices : [] // Return an array of indices where the pattern is found or empty array if not found + return indices } function hash(str, length) { From 952fbd4ea1c08f7d85255191e4e2992d73a8f41d Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:06:00 +0000 Subject: [PATCH 7/7] Updated Documentation in README.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index aea7b1548f..cd4d244e31 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -166,6 +166,7 @@ * [Area](Maths/Area.js) * [ArithmeticGeometricMean](Maths/ArithmeticGeometricMean.js) * [ArmstrongNumber](Maths/ArmstrongNumber.js) + * [AutomorphicNumber](Maths/AutomorphicNumber.js) * [AverageMean](Maths/AverageMean.js) * [AverageMedian](Maths/AverageMedian.js) * [BinaryConvert](Maths/BinaryConvert.js) @@ -308,6 +309,7 @@ * [LinearSearch](Search/LinearSearch.js) * [Minesweeper](Search/Minesweeper.js) * [QuickSelectSearch](Search/QuickSelectSearch.js) + * [RabinKarp](Search/RabinKarp.js) * [SlidingWindow](Search/SlidingWindow.js) * [StringSearch](Search/StringSearch.js) * [TernarySearch](Search/TernarySearch.js)