Source: diff/lib/Lcs.js

  1. import {DiffConfig} from '../../config/DiffConfig.js';
  2. /**
  3. * Compute the length of the longest common subsequence (LCS) between two
  4. * sequences. A simple dynamic programming approach is employed, which yields
  5. * O(n*m) time complexity and O(n*m) space complexity.
  6. * @param {Array<any>} seqA The first sequence.
  7. * @param {Array<any>} seqB The second sequence.
  8. * @param {Function} compare The comparator function used to identify equal
  9. * elements between the sequences. Defaults to the built-in strict equality
  10. * operator ("===").
  11. * @return {Number} The length of the LCS.
  12. */
  13. export function getLcsLength(seqA, seqB, compare = (a, b) => a === b) {
  14. // Initial 2D array of size (m + 1) * (n + 1)
  15. const dp = new Array(seqA.length + 1);
  16. for (let i = 0; i < seqA.length + 1; i++) {
  17. dp[i] = new Array(seqB.length + 1);
  18. }
  19. // The LCS of any sequence with a sequence of length zero
  20. // also has length zero
  21. for (let i = 0; i <= seqA.length; i++) {
  22. dp[i][0] = 0;
  23. }
  24. for (let i = 0; i <= seqB.length; i++) {
  25. dp[0][i] = 0;
  26. }
  27. // We expect most inputs to yield a short LCS, so a bottom-up approach is
  28. // preferred.
  29. for (let i = 1; i <= seqA.length; i++) {
  30. for (let j = 1; j <= seqB.length; j++) {
  31. if (compare(seqA[i - 1], seqB[j - 1])) {
  32. dp[i][j] = dp[i - 1][j - 1] + 1;
  33. } else if (dp[i - 1][j] > dp[i][j - 1]) {
  34. dp[i][j] = dp[i - 1][j];
  35. } else {
  36. dp[i][j] = dp[i][j - 1];
  37. }
  38. }
  39. }
  40. return dp[seqA.length][seqB.length];
  41. }
  42. // Initial 2D array of size (r + 1) * (r + 1)
  43. const dp = new Array(DiffConfig.COMPARATOR.PATH_COMPARE_RANGE + 1);
  44. for (let i = 0; i < dp.length; i++) {
  45. dp[i] = new Array(dp.length);
  46. }
  47. /**
  48. * Faster LCS computation for sequences of maximum length r (path compare
  49. * range) due to matrix reuse.
  50. * @param {Array<Number>} seqA The first sequence.
  51. * @param {Array<Number>} seqB The second sequence.
  52. * @return {Number} The length of the LCS.
  53. */
  54. export function getLcsLengthFast(seqA, seqB) {
  55. // The LCS of any sequence with a sequence of length zero
  56. // also has length zero
  57. for (let i = 0; i <= seqA.length; i++) {
  58. dp[i][0] = 0;
  59. }
  60. for (let i = 0; i <= seqB.length; i++) {
  61. dp[0][i] = 0;
  62. }
  63. // We expect most inputs to yield a short LCS, so a bottom-up approach is
  64. // preferred.
  65. for (let i = 1; i <= seqA.length; i++) {
  66. for (let j = 1; j <= seqB.length; j++) {
  67. if (seqA[i - 1] === seqB[j - 1]) {
  68. dp[i][j] = dp[i - 1][j - 1] + 1;
  69. } else if (dp[i - 1][j] > dp[i][j - 1]) {
  70. dp[i][j] = dp[i - 1][j];
  71. } else {
  72. dp[i][j] = dp[i][j - 1];
  73. }
  74. }
  75. }
  76. return dp[seqA.length][seqB.length];
  77. }