@@ -149,34 +149,84 @@ const assertCommonItems = (
149149 }
150150} ;
151151
152+ // Given lengths of sequences and input function to compare items at indexes,
153+ // return number of differences according to baseline greedy forward algorithm.
154+ const countDifferences = (
155+ aLength : number ,
156+ bLength : number ,
157+ isCommon ,
158+ ) : number => {
159+ const dMax = aLength + bLength ;
160+ const aIndexes = [ - 1 ] ; // initialize for aLast + 1 in loop when d = 0
161+
162+ for ( let d = 0 ; d <= dMax ; d += 1 ) {
163+ let aIndexPrev1 = 0 ; // that is, not yet set
164+
165+ for ( let iF = 0 , kF = - d ; iF <= d ; iF += 1 , kF += 2 ) {
166+ const aFirst =
167+ iF === 0 || ( iF !== d && aIndexPrev1 < aIndexes [ iF ] )
168+ ? aIndexes [ iF ] // vertical to insert from b
169+ : aIndexPrev1 + 1 ; // horizontal to delete from a
170+
171+ // To get last point of path segment, move along diagonal of common items.
172+ let aLast = aFirst ;
173+ let bLast = aFirst - kF ;
174+ while (
175+ aLast + 1 < aLength &&
176+ bLast + 1 < bLength &&
177+ isCommon ( aLast + 1 , bLast + 1 )
178+ ) {
179+ aLast += 1 ;
180+ bLast += 1 ;
181+ }
182+
183+ aIndexPrev1 = aIndexes [ iF ] ;
184+ aIndexes [ iF ] = aLast ;
185+
186+ if ( aLast === aLength - 1 && bLast === bLength - 1 ) {
187+ return d ;
188+ }
189+ }
190+ }
191+ throw new Error ( `countDifferences did not return a number` ) ;
192+ } ;
193+
152194// Return array of items in a longest common subsequence of array-like objects.
153195const findCommonItems = (
154196 a : Array < any > | string ,
155197 b : Array < any > | string ,
156198) : Array < any > => {
199+ const aLength = a . length ;
200+ const bLength = b . length ;
201+ const isCommon = ( aIndex : number , bIndex : number ) => {
202+ assertMin ( 'input aIndex' , aIndex , 0 ) ;
203+ assertEnd ( 'input aIndex' , aIndex , aLength ) ;
204+ assertMin ( 'input bIndex' , bIndex , 0 ) ;
205+ assertEnd ( 'input bIndex' , bIndex , bLength ) ;
206+ return a [ aIndex ] === b [ bIndex ] ;
207+ } ;
208+
157209 const array = [ ] ;
158210 diff (
159- a . length ,
160- b . length ,
161- ( aIndex : number , bIndex : number ) => {
162- assertMin ( 'input aIndex' , aIndex , 0 ) ;
163- assertEnd ( 'input aIndex' , aIndex , a . length ) ;
164- assertMin ( 'input bIndex' , bIndex , 0 ) ;
165- assertEnd ( 'input bIndex' , bIndex , b . length ) ;
166- return a [ aIndex ] === b [ bIndex ] ;
167- } ,
211+ aLength ,
212+ bLength ,
213+ isCommon ,
168214 ( nCommon : number , aCommon : number , bCommon : number ) => {
169215 assertMin ( 'output nCommon' , nCommon , 1 ) ;
170216 assertMin ( 'output aCommon' , aCommon , 0 ) ;
171- assertMax ( 'output aCommon + nCommon' , aCommon + nCommon , a . length ) ;
217+ assertMax ( 'output aCommon + nCommon' , aCommon + nCommon , aLength ) ;
172218 assertMin ( 'output bCommon' , bCommon , 0 ) ;
173- assertMax ( 'output bCommon + nCommon' , bCommon + nCommon , b . length ) ;
219+ assertMax ( 'output bCommon + nCommon' , bCommon + nCommon , bLength ) ;
174220 assertCommonItems ( a , b , nCommon , aCommon , bCommon ) ;
175221 for ( ; nCommon !== 0 ; nCommon -= 1 , aCommon += 1 ) {
176222 array . push ( a [ aCommon ] ) ;
177223 }
178224 } ,
179225 ) ;
226+
227+ const nDifferences = countDifferences ( aLength , bLength , isCommon ) ;
228+ expect ( aLength + bLength - 2 * array . length ) . toBe ( nDifferences ) ;
229+
180230 return array ;
181231} ;
182232
0 commit comments