22 *
33 * @typedef {Object } PatternTable
44 * @property {Object[] } badCharacter badCharacter
5- * @property {number[] } goodSuffix goodSuffix
6- * @property {number } length
5+ * @property {number[] } L goodSuffix L array
6+ * L[i] is the largest position less than n - 1 such that string P[i..n-1] matches a suffix of P[0..L[i]]
7+ * If none exists,L[i] is defined to be -1 .
8+ * @property {number[] } H goodSuffix H array
9+ * H[i] is the length of the largest suffix of P[i..n-1] that is also a prefix of P, if one exists.
10+ * If none exists,H[i] is defined to be -1 .
11+ * @property {number[] } goodSuffixOffset goodSuffixOffset calc by L and H
12+ * @property {number } length length of pattern
713 */
814
915/**
@@ -18,28 +24,66 @@ function buildPattern(pattern) {
1824 for ( let i = 1 ; i < pattern . length ; i ++ ) {
1925 badCharacter [ i ] = { }
2026 Object . assign ( badCharacter [ i ] , badCharacter [ i - 1 ] ) ;
21- badCharacter [ i ] [ pattern [ i - 1 ] ] = i - 1 ;
27+ badCharacter [ i ] [ pattern [ i - 1 ] ] = ( i - 1 ) ;
2228 }
23- let goodSuffix = new Array ( ) . fill ( 0 ) ;
24- // console.table(badCharacter);
25- // console.table(goodSuffix);
29+
30+ // Both of these tables are constructible in O(n) time and use O(n) space.
31+ let L = new Array ( length ) . fill ( - 1 ) ;
32+ let lastIndex = pattern . length - 1 ;
33+ let j = lastIndex ; // good suffix index
34+ let i = lastIndex - 1 ;
35+ while ( i >= 0 && j > 0 ) {
36+ let l = i + ( lastIndex - j ) ; // last index of current frame.
37+ if ( l < j ) {
38+ if ( pattern [ i ] === pattern [ j ] ) {
39+ L [ j ] = ( L [ j ] === - 1 ? l : L [ j ] ) ;
40+ j -- ;
41+ }
42+ i -- ;
43+ } else {
44+ i = j - 1 ;
45+ j = lastIndex ;
46+ }
47+ }
48+ // H should only be used if L[i] is -1 or a match has been found.
49+ let H = new Array ( length ) . fill ( - 1 ) ;
50+ j = lastIndex ; // good suffix index
51+ for ( ; j >= 0 ; j -- ) {
52+ let goodSuffixMaxSuffixLength = j + 1 ;
53+ let maxSuffixLength = length - goodSuffixMaxSuffixLength ;
54+ if ( pattern . substr ( 0 , maxSuffixLength ) === pattern . substr ( goodSuffixMaxSuffixLength , maxSuffixLength ) ) {
55+ H [ j ] = maxSuffixLength - 1 ;
56+ } else {
57+ H [ j ] = H [ j + 1 ] === undefined ? - 1 : H [ j + 1 ] ;
58+ }
59+ }
60+ // The alignment shift for index i in P is given by n - 1 - L[i] or n - 1 - H[i].
61+ let goodSuffixOffset = L . map ( ( value , index ) => {
62+ if ( value === - 1 ) {
63+ value = H [ index ] ;
64+ }
65+ return length - 1 - value ;
66+ } ) ;
67+
2668 return {
27- length,
2869 badCharacter,
29- goodSuffix,
70+ goodSuffixOffset,
71+ L,
72+ H,
3073 } ;
3174}
3275
3376/**
3477 * calc offset from pattern table
3578 * @param {PatternTable } patternTable pattern table
36- * @param {number } dismatchIndex dismatch index
37- * @param {string } dismatchChar dismatch character
79+ * @param {number } mismatchIndex mismatchIndex in pattern
80+ * @param {string } badChar mismatch character in text
3881 */
39- function getOffset ( patternTable , dismatchIndex , badChar ) {
40- let posBadChar = patternTable . badCharacter [ dismatchIndex ] [ badChar ] === undefined ? - 1 : patternTable . badCharacter [ dismatchIndex ] [ badChar ] ;
41- return dismatchIndex - posBadChar ;
42- // return Math.max(dismatchIndex - posBadChar, patternTable.goodSuffix[dismatchIndex]);
82+ function getOffset ( patternTable , mismatchIndex , badChar ) {
83+ let badCharIndex = patternTable . badCharacter [ mismatchIndex ] [ badChar ] === undefined ? - 1 : patternTable . badCharacter [ mismatchIndex ] [ badChar ] ;
84+ let badCharOffset = mismatchIndex - badCharIndex ;
85+ let goodSuffixOffset = patternTable . goodSuffixOffset [ mismatchIndex + 1 ] || badCharOffset ;
86+ return Math . max ( badCharOffset , goodSuffixOffset ) ;
4387}
4488
4589/**
@@ -53,22 +97,27 @@ function boyerMoore(text, pattern) {
5397 return 0 ;
5498 }
5599 const patternTable = buildPattern ( pattern ) ;
56- let patternLastIndex = pattern . length - 1 ;
57- let pIndex = patternLastIndex ;
58- let tIndex = pIndex ;
59- while ( tIndex < text . length ) {
60- if ( text [ tIndex ] === pattern [ pIndex ] ) {
100+ let pLastIndex = pattern . length - 1 ; // pattern last index
101+ let fLastIndex = pLastIndex ; // frame last index
102+ let comparedCount = 0 ; // compared count beween pattern and frame
103+ while ( fLastIndex < text . length ) {
104+ let pIndex = pLastIndex - comparedCount ; // calc current compare index in pattern;
105+ let fIndex = fLastIndex - comparedCount ; // calc current compare index in frame;
106+ if ( text [ fIndex ] === pattern [ pIndex ] ) {
61107 if ( pIndex === 0 ) {
62- return tIndex ;
108+ return fIndex ;
63109 }
64- tIndex -- ;
65- pIndex -- ;
110+ comparedCount ++ ;
66111 } else {
67- tIndex = tIndex + ( patternLastIndex - pIndex ) + getOffset ( patternTable , pIndex , text [ tIndex ] ) ;
68- pIndex = patternLastIndex ;
112+ // not match reset compared count and go to next frame
113+ fLastIndex = fLastIndex + getOffset ( patternTable , pIndex , text [ fIndex ] ) ;
114+ comparedCount = 0 ;
69115 }
70116 }
71117 return - 1 ;
72118}
73119
74- module . exports = boyerMoore ;
120+ module . exports = {
121+ boyerMoore,
122+ buildPattern
123+ } ;
0 commit comments