Skip to content

Rabin Karp Search Algorithm #1545

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 7 commits into from
Oct 30, 2023
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
2 changes: 1 addition & 1 deletion Bit-Manipulation/BinaryCountSetBits.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function BinaryCountSetBits(a) {

let count = 0
while (a) {
a &= (a - 1)
a &= a - 1
count++
}

Expand Down
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Maths/AutomorphicNumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ export const isAutomorphic = (n) => {
n = Math.floor(n / 10)
n_sq = Math.floor(n_sq / 10)
}

return true
}
16 changes: 8 additions & 8 deletions Maths/test/AutomorphicNumber.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand Down
2 changes: 1 addition & 1 deletion Search/InterpolationSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ export function interpolationSearch(arr, key) {
}

return -1
}
}
64 changes: 64 additions & 0 deletions Search/RabinKarp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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)
*/

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 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
}

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 }
30 changes: 30 additions & 0 deletions Search/test/RabinKarp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +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 = []

const result = rabinKarpSearch(text, pattern)
expect(result).to.deep.equal(expected)
})
})