From 0d547721d7e0d5c6bdcd319711157b4f5303bb12 Mon Sep 17 00:00:00 2001 From: Arye Lukashevski Date: Thu, 28 Jun 2018 15:27:41 +0300 Subject: [PATCH] change backtrack to be iterative --- src/filters/lcs.js | 68 ++++++++++++++++++++++------------------------ test/index.js | 17 ++++++++++++ 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/filters/lcs.js b/src/filters/lcs.js index 857275a1..fc4b6889 100644 --- a/src/filters/lcs.js +++ b/src/filters/lcs.js @@ -7,7 +7,7 @@ reference: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem */ const defaultMatch = function(array1, array2, index1, index2) { - return array1[index1] === array2[index2]; + return array1[ index1 ] === array2[ index2 ]; }; const lengthMatrix = function(array1, array2, match, context) { @@ -16,11 +16,11 @@ const lengthMatrix = function(array1, array2, match, context) { let x, y; // initialize empty matrix of len1+1 x len2+1 - let matrix = [len1 + 1]; + let matrix = [ len1 + 1 ]; for (x = 0; x < len1 + 1; x++) { - matrix[x] = [len2 + 1]; + matrix[ x ] = [ len2 + 1 ]; for (y = 0; y < len2 + 1; y++) { - matrix[x][y] = 0; + matrix[ x ][ y ] = 0; } } matrix.match = match; @@ -28,44 +28,44 @@ const lengthMatrix = function(array1, array2, match, context) { for (x = 1; x < len1 + 1; x++) { for (y = 1; y < len2 + 1; y++) { if (match(array1, array2, x - 1, y - 1, context)) { - matrix[x][y] = matrix[x - 1][y - 1] + 1; + matrix[ x ][ y ] = matrix[ x - 1 ][ y - 1 ] + 1; } else { - matrix[x][y] = Math.max(matrix[x - 1][y], matrix[x][y - 1]); + matrix[ x ][ y ] = Math.max(matrix[ x - 1 ][ y ], matrix[ x ][ y - 1 ]); } } } return matrix; }; -const backtrack = function(matrix, array1, array2, index1, index2, context) { - if (index1 === 0 || index2 === 0) { - return { - sequence: [], - indices1: [], - indices2: [], - }; - } - - if (matrix.match(array1, array2, index1 - 1, index2 - 1, context)) { - const subsequence = backtrack( - matrix, - array1, - array2, - index1 - 1, - index2 - 1, - context - ); - subsequence.sequence.push(array1[index1 - 1]); - subsequence.indices1.push(index1 - 1); - subsequence.indices2.push(index2 - 1); - return subsequence; - } +const backtrack = function(matrix, array1, array2, context) { + let index1 = array1.length; + let index2 = array2.length; + const subsequence = { + sequence: [], + indices1: [], + indices2: [], + }; - if (matrix[index1][index2 - 1] > matrix[index1 - 1][index2]) { - return backtrack(matrix, array1, array2, index1, index2 - 1, context); - } else { - return backtrack(matrix, array1, array2, index1 - 1, index2, context); + while (index1 !== 0 && index2 !== 0) { + const sameLetter = + matrix.match(array1, array2, index1 - 1, index2 - 1, context); + if (sameLetter) { + subsequence.sequence.unshift(array1[ index1 - 1 ]); + subsequence.indices1.unshift(index1 - 1); + subsequence.indices2.unshift(index2 - 1); + --index1; + --index2; + } else { + const valueAtMatrixAbove = matrix[ index1 ][ index2 - 1 ]; + const valueAtMatrixLeft = matrix[ index1 - 1 ][ index2 ]; + if (valueAtMatrixAbove > valueAtMatrixLeft) { + --index2; + } else { + --index1; + } + } } + return subsequence; }; const get = function(array1, array2, match, context) { @@ -80,8 +80,6 @@ const get = function(array1, array2, match, context) { matrix, array1, array2, - array1.length, - array2.length, innerContext ); if (typeof array1 === 'string' && typeof array2 === 'string') { diff --git a/test/index.js b/test/index.js index 85fcd381..136583f7 100644 --- a/test/index.js +++ b/test/index.js @@ -751,4 +751,21 @@ describe('lcs', () => { indices2: [1], }); }); + + it('should compute diff for large array', () => { + const ARRAY_LENGTH = 5000; // js stack is about 50k + function randomArray() { + let result = []; + for (let i = 0; i < ARRAY_LENGTH; i++) { + if (Math.random() > 0.5) { + result.push('A'); + } else { + result.push('B'); + } + } + return result; + } + + lcs.get(randomArray(), randomArray()); + }); });