Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 593ac64

Browse files
committedJul 25, 2018
Updated Rabin-Karp search to use rolling hash module
1 parent 439df28 commit 593ac64

File tree

2 files changed

+18
-77
lines changed

2 files changed

+18
-77
lines changed
 

‎src/algorithms/string/rabin-karp/__test__/rabinKarp.test.js

+2-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
1-
import { rabinKarp, hashWord, reHashWord } from '../rabinKarp';
1+
import rabinKarp from '../rabinKarp';
22

33
describe('rabinKarp', () => {
4-
it('should correctly calculates hash and re-hash', () => {
5-
expect(hashWord('a')).toBe(97);
6-
expect(hashWord('b')).toBe(98);
7-
expect(hashWord('abc')).toBe(941094);
8-
expect(hashWord('bcd')).toBe(950601);
9-
expect(reHashWord(hashWord('abc'), 'abc', 'bcd')).toBe(950601);
10-
expect(reHashWord(hashWord('abc'), 'abc', 'bcd')).toBe(hashWord('bcd'));
11-
});
12-
134
it('should find substring in a string', () => {
145
expect(rabinKarp('', '')).toBe(0);
156
expect(rabinKarp('a', '')).toBe(0);
167
expect(rabinKarp('a', 'a')).toBe(0);
8+
expect(rabinKarp('ab', 'b')).toBe(1);
179
expect(rabinKarp('abcbcglx', 'abca')).toBe(-1);
1810
expect(rabinKarp('abcbcglx', 'bcgl')).toBe(3);
1911
expect(rabinKarp('abcxabcdabxabcdabcdabcy', 'abcdabcy')).toBe(15);

‎src/algorithms/string/rabin-karp/rabinKarp.js

+16-67
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,26 @@
1-
/**
2-
* A prime number used to create
3-
* the hash representation of a word
4-
*
5-
* Bigger the prime number,
6-
* bigger the hash value
7-
*/
8-
const PRIME = 97;
9-
10-
/**
11-
* Function that creates hash representation of the word.
12-
*
13-
* @param {string} word
14-
* @return {number}
15-
*/
16-
export function hashWord(word) {
17-
let hash = 0;
18-
19-
for (let charIndex = 0; charIndex < word.length; charIndex += 1) {
20-
hash += word.charCodeAt(charIndex) * (PRIME ** charIndex);
21-
}
22-
23-
return hash;
24-
}
25-
26-
/**
27-
* Function that creates hash representation of the word
28-
* based on previous word (shifted by one character left) hash value.
29-
*
30-
* Recalculates the hash representation of a word so that it isn't
31-
* necessary to traverse the whole word again
32-
*
33-
* @param {number} prevHash
34-
* @param {string} prevWord
35-
* @param {string} newWord
36-
* @return {number}
37-
*/
38-
export function reHashWord(prevHash, prevWord, newWord) {
39-
const newWordLastIndex = newWord.length - 1;
40-
let newHash = prevHash - prevWord.charCodeAt(0);
41-
newHash /= PRIME;
42-
newHash += newWord.charCodeAt(newWordLastIndex) * (PRIME ** newWordLastIndex);
43-
44-
return newHash;
45-
}
1+
import RabinFingerprint from '../../../utils/hash/rolling/Rabin_Fingerprint';
462

473
/**
484
* @param {string} text
495
* @param {string} word
506
* @return {number}
517
*/
52-
export function rabinKarp(text, word) {
53-
// Calculate word hash that we will use for comparison with other substring hashes.
54-
const wordHash = hashWord(word);
55-
56-
let prevSegment = null;
57-
let currentSegmentHash = null;
58-
59-
// Go through all substring of the text that may match
60-
for (let charIndex = 0; charIndex <= text.length - word.length; charIndex += 1) {
61-
const currentSegment = text.substring(charIndex, charIndex + word.length);
62-
63-
// Calculate the hash of current substring.
64-
if (currentSegmentHash === null) {
65-
currentSegmentHash = hashWord(currentSegment);
66-
} else {
67-
currentSegmentHash = reHashWord(currentSegmentHash, prevSegment, currentSegment);
68-
}
69-
70-
prevSegment = currentSegment;
8+
export default function rabinKarp(text, word) {
9+
// The prime generation function could depend on the inputs for collision guarantees.
10+
const hasher = new RabinFingerprint(() => 229);
11+
const toCode = character => character.codePointAt(0);
12+
const cmpVal = hasher.init(Array.from(word).map(toCode));
13+
14+
let currHash = hasher.init(Array.from(text.substring(0, word.length)).map(toCode));
15+
if ((currHash === cmpVal) && (word.valueOf() === text.substring(0, word.length).valueOf())) {
16+
return 0;
17+
}
7118

72-
// Compare the hash of current substring and seeking string.
73-
if ((wordHash === currentSegmentHash) && (currentSegment.valueOf() === word.valueOf())) {
74-
return charIndex;
19+
for (let i = 0; i < (text.length - word.length); i += 1) {
20+
currHash = hasher.roll(text.codePointAt(i), text.codePointAt(i + word.length));
21+
if ((currHash === cmpVal)
22+
&& (word.valueOf() === text.substring(i + 1, i + word.length + 1).valueOf())) {
23+
return i + 1;
7524
}
7625
}
7726

0 commit comments

Comments
 (0)
Please sign in to comment.