@@ -112,3 +112,107 @@ $.extend( $.ui.autocomplete, {
112
112
} ) ;
113
113
}
114
114
} ) ;
115
+
116
+ /*
117
+ * Courtesy https://siderite.blogspot.com/2014/11/super-fast-and-accurate-string-distance.html
118
+ */
119
+
120
+ // Sift4 - common version
121
+ // online algorithm to compute the distance between two strings in O(n)
122
+ // maxOffset is the number of characters to search for matching letters
123
+ // maxDistance is the distance at which the algorithm should stop computing the value and just exit (the strings are too different anyway)
124
+ function sift4 ( s1 , s2 , maxOffset , maxDistance ) {
125
+ if ( ! s1 || ! s1 . length ) {
126
+ if ( ! s2 ) {
127
+ return 0 ;
128
+ }
129
+ return s2 . length ;
130
+ }
131
+
132
+ if ( ! s2 || ! s2 . length ) {
133
+ return s1 . length ;
134
+ }
135
+
136
+ var l1 = s1 . length ;
137
+ var l2 = s2 . length ;
138
+
139
+ var c1 = 0 ; //cursor for string 1
140
+ var c2 = 0 ; //cursor for string 2
141
+ var lcss = 0 ; //largest common subsequence
142
+ var local_cs = 0 ; //local common substring
143
+ var trans = 0 ; //number of transpositions ('ab' vs 'ba')
144
+ var offset_arr = [ ] ; //offset pair array, for computing the transpositions
145
+
146
+ while ( ( c1 < l1 ) && ( c2 < l2 ) ) {
147
+ if ( s1 . charAt ( c1 ) == s2 . charAt ( c2 ) ) {
148
+ local_cs ++ ;
149
+ var isTrans = false ;
150
+ //see if current match is a transposition
151
+ var i = 0 ;
152
+ while ( i < offset_arr . length ) {
153
+ var ofs = offset_arr [ i ] ;
154
+ if ( c1 <= ofs . c1 || c2 <= ofs . c2 ) {
155
+ // when two matches cross, the one considered a transposition is the one with the largest difference in offsets
156
+ isTrans = Math . abs ( c2 - c1 ) >= Math . abs ( ofs . c2 - ofs . c1 ) ;
157
+ if ( isTrans )
158
+ {
159
+ trans ++ ;
160
+ } else
161
+ {
162
+ if ( ! ofs . trans ) {
163
+ ofs . trans = true ;
164
+ trans ++ ;
165
+ }
166
+ }
167
+ break ;
168
+ } else {
169
+ if ( c1 > ofs . c2 && c2 > ofs . c1 ) {
170
+ offset_arr . splice ( i , 1 ) ;
171
+ } else {
172
+ i ++ ;
173
+ }
174
+ }
175
+ }
176
+ offset_arr . push ( {
177
+ c1 :c1 ,
178
+ c2 :c2 ,
179
+ trans :isTrans
180
+ } ) ;
181
+ } else {
182
+ lcss += local_cs ;
183
+ local_cs = 0 ;
184
+ if ( c1 != c2 ) {
185
+ c1 = c2 = Math . min ( c1 , c2 ) ; //using min allows the computation of transpositions
186
+ }
187
+ //if matching characters are found, remove 1 from both cursors (they get incremented at the end of the loop)
188
+ //so that we can have only one code block handling matches
189
+ for ( var i = 0 ; i < maxOffset && ( c1 + i < l1 || c2 + i < l2 ) ; i ++ ) {
190
+ if ( ( c1 + i < l1 ) && ( s1 . charAt ( c1 + i ) == s2 . charAt ( c2 ) ) ) {
191
+ c1 += i - 1 ;
192
+ c2 -- ;
193
+ break ;
194
+ }
195
+ if ( ( c2 + i < l2 ) && ( s1 . charAt ( c1 ) == s2 . charAt ( c2 + i ) ) ) {
196
+ c1 -- ;
197
+ c2 += i - 1 ;
198
+ break ;
199
+ }
200
+ }
201
+ }
202
+ c1 ++ ;
203
+ c2 ++ ;
204
+ if ( maxDistance )
205
+ {
206
+ var temporaryDistance = Math . max ( c1 , c2 ) - lcss + trans ;
207
+ if ( temporaryDistance >= maxDistance ) return Math . round ( temporaryDistance ) ;
208
+ }
209
+ // this covers the case where the last match is on the last token in list, so that it can compute transpositions correctly
210
+ if ( ( c1 >= l1 ) || ( c2 >= l2 ) ) {
211
+ lcss += local_cs ;
212
+ local_cs = 0 ;
213
+ c1 = c2 = Math . min ( c1 , c2 ) ;
214
+ }
215
+ }
216
+ lcss += local_cs ;
217
+ return Math . round ( Math . max ( l1 , l2 ) - lcss + trans ) ; //add the cost of transpositions to the final result
218
+ }
0 commit comments