forked from trekhleb/javascript-algorithms
-
Notifications
You must be signed in to change notification settings - Fork 2
/
rabinKarp.js
90 lines (76 loc) · 2.38 KB
/
rabinKarp.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* A prime number used to create
* the hash representation of a word
*
* Bigger the prime number,
* bigger the hash value
*/
const PRIME = 97;
/**
* Function that creates hash representation of the word.
*
* @param {string} word
* @return {number}
*/
export function hashWord(word) {
let hash = 0;
for (let charIndex = 0; charIndex < word.length; charIndex += 1) {
hash += word[charIndex].charCodeAt(0) * (PRIME ** charIndex);
}
return hash;
}
/**
* Function that creates hash representation of the word
* based on previous word (shifted by one character left) hash value.
*
* Recalculates the hash representation of a word so that it isn't
* necessary to traverse the whole word again
*
* @param {number} prevHash
* @param {string} prevWord
* @param {string} newWord
* @return {number}
*/
export function reHashWord(prevHash, prevWord, newWord) {
const newWordLastIndex = newWord.length - 1;
let newHash = prevHash - prevWord[0].charCodeAt(0);
newHash /= PRIME;
newHash += newWord[newWordLastIndex].charCodeAt(0) * (PRIME ** newWordLastIndex);
return newHash;
}
/**
* @param {string} text
* @param {string} word
* @return {number}
*/
export function rabinKarp(text, word) {
// Calculate word hash that we will use for comparison with other substring hashes.
const wordHash = hashWord(word);
let prevSegment = null;
let currentSegmentHash = null;
// Go through all substring of the text that may match
for (let charIndex = 0; charIndex <= text.length - word.length; charIndex += 1) {
const currentSegment = text.substring(charIndex, charIndex + word.length);
// Calculate the hash of current substring.
if (currentSegmentHash === null) {
currentSegmentHash = hashWord(currentSegment);
} else {
currentSegmentHash = reHashWord(currentSegmentHash, prevSegment, currentSegment);
}
prevSegment = currentSegment;
// Compare the hash of current substring and seeking string.
if (wordHash === currentSegmentHash) {
// In case if hashes match let's check substring char by char.
let numberOfMatches = 0;
for (let deepCharIndex = 0; deepCharIndex < word.length; deepCharIndex += 1) {
if (word[deepCharIndex] === text[charIndex + deepCharIndex]) {
numberOfMatches += 1;
}
}
if (numberOfMatches === word.length) {
return charIndex;
}
}
}
return -1;
}