1+ export function getRabinKarpSteps ( text , pattern ) {
2+ const steps = [ ] ;
3+ const n = text . length ;
4+ const m = pattern . length ;
5+
6+ if ( m === 0 || n < m ) {
7+ return { steps, found : false } ;
8+ }
9+
10+ const d = 256 ;
11+ const q = 101 ;
12+ let h = 1 ;
13+ let pHash = 0 ;
14+ let tHash = 0 ;
15+ let found = false ;
16+
17+ for ( let i = 0 ; i < m - 1 ; i ++ ) {
18+ h = ( h * d ) % q ;
19+ }
20+
21+ steps . push ( {
22+ windowStart : 0 ,
23+ compareIndex : - 1 ,
24+ status : 'calculating' ,
25+ pHash : 0 ,
26+ tHash : 0 ,
27+ message : `Starting. Pattern length=${ m } , Text length=${ n } . Prime q=${ q } . Multiplier h=${ h } .`
28+ } ) ;
29+
30+ for ( let i = 0 ; i < m ; i ++ ) {
31+ pHash = ( d * pHash + pattern . charCodeAt ( i ) ) % q ;
32+ tHash = ( d * tHash + text . charCodeAt ( i ) ) % q ;
33+
34+ steps . push ( {
35+ windowStart : 0 ,
36+ compareIndex : i ,
37+ status : 'calculating' ,
38+ pHash,
39+ tHash,
40+ message : `Calculating initial hashes. Index ${ i } . Pattern Hash = ${ pHash } , Window Hash = ${ tHash } .`
41+ } ) ;
42+ }
43+
44+ for ( let i = 0 ; i <= n - m ; i ++ ) {
45+ steps . push ( {
46+ windowStart : i ,
47+ compareIndex : - 1 ,
48+ status : 'sliding' ,
49+ pHash,
50+ tHash,
51+ message : `Sliding window to index ${ i } . Pattern Hash = ${ pHash } , Window Hash = ${ tHash } .`
52+ } ) ;
53+
54+ if ( pHash === tHash ) {
55+ steps . push ( {
56+ windowStart : i ,
57+ compareIndex : 0 ,
58+ status : 'hash_match' ,
59+ pHash,
60+ tHash,
61+ message : `Hash Match Found! Verifying character by character...`
62+ } ) ;
63+
64+ let j = 0 ;
65+ for ( j = 0 ; j < m ; j ++ ) {
66+ steps . push ( {
67+ windowStart : i ,
68+ compareIndex : i + j ,
69+ patternCompareIndex : j ,
70+ status : 'verifying' ,
71+ pHash,
72+ tHash,
73+ message : `Verifying: text[${ i + j } ] ('${ text [ i + j ] } ') === pattern[${ j } ] ('${ pattern [ j ] } ')?`
74+ } ) ;
75+
76+ if ( text [ i + j ] !== pattern [ j ] ) {
77+ steps . push ( {
78+ windowStart : i ,
79+ compareIndex : i + j ,
80+ patternCompareIndex : j ,
81+ status : 'spurious_hit' ,
82+ pHash,
83+ tHash,
84+ message : `Spurious Hit! Mismatch found. Resuming search.`
85+ } ) ;
86+ break ;
87+ }
88+ }
89+
90+ if ( j === m ) {
91+ found = true ;
92+ steps . push ( {
93+ windowStart : i ,
94+ compareIndex : - 1 ,
95+ status : 'found' ,
96+ pHash,
97+ tHash,
98+ message : `Pattern Found at index ${ i } !`
99+ } ) ;
100+ }
101+ }
102+
103+ if ( i < n - m ) {
104+ const oldHash = tHash ;
105+ const charToRemove = text . charCodeAt ( i ) ;
106+ const charToAdd = text . charCodeAt ( i + m ) ;
107+
108+ tHash = ( d * ( tHash - charToRemove * h ) + charToAdd ) % q ;
109+ if ( tHash < 0 ) {
110+ tHash += q ;
111+ }
112+
113+ steps . push ( {
114+ windowStart : i ,
115+ compareIndex : - 1 ,
116+ status : 'rolling' ,
117+ removeIndex : i ,
118+ addIndex : i + m ,
119+ pHash,
120+ tHash : oldHash ,
121+ message : `Rolling hash... Removing '${ text [ i ] } ' (val ${ charToRemove } ).`
122+ } ) ;
123+
124+ steps . push ( {
125+ windowStart : i + 1 ,
126+ compareIndex : - 1 ,
127+ status : 'rolling' ,
128+ removeIndex : i ,
129+ addIndex : i + m ,
130+ pHash,
131+ tHash,
132+ message : `Rolling hash... Adding '${ text [ i + m ] } ' (val ${ charToAdd } ). New Window Hash = ${ tHash } .`
133+ } ) ;
134+ }
135+ }
136+
137+ steps . push ( {
138+ windowStart : - 1 ,
139+ compareIndex : - 1 ,
140+ status : found ? 'finished_found' : 'finished_not_found' ,
141+ pHash,
142+ tHash,
143+ message : found ? "Search complete. Pattern was found." : "Search complete. Pattern was not found."
144+ } ) ;
145+
146+ return { steps, found } ;
147+ }
0 commit comments