UNPKG

musicvis-lib

Version:

Music analysis and visualization library

121 lines (117 loc) 3.26 kB
/** * @module stringBased/LongestCommonSubsequence */ /** * Calculates the longest common subsequence. * * @see https://rosettacode.org/wiki/Longest_common_subsequence#JavaScript * @example * const lcs = StringBased.LongestCommonSubsequence.lcs('hello world!', 'world'); * // world * @param {string|Array} a a string * @param {string|Array} b another string * @returns {string|Array} the longest common subsequence */ export function lcs (a, b) { // Make sure shorter string is the column string const m = a.length const n = b.length // Return now if one (or both) empty if (a.length === 0) { return a } if (b.length === 0) { return b } let i; let j; let row = []; let left; let diagonal; let latch const lcs = [] const c = [] // Build the c-table for (j = 0; j < n; row[j++] = 0); for (i = 0; i < m; i++) { c[i] = row = [...row] for (diagonal = 0, j = 0; j < n; j++, diagonal = latch) { latch = row[j] if (a[i] === b[j]) { row[j] = diagonal + 1 } else { left = row[j - 1] || 0 if (left > row[j]) { row[j] = left } } } } i-- j-- // row[j] now contains the length of the lcs // Recover the lcs from the table while (i > -1 && j > -1) { switch (c[i][j]) { // eslint-disable-next-line default: j-- lcs.unshift(a[i]) case (i && c[i - 1][j]): // eslint-disable-line no-fallthrough i-- continue case (j && c[i][j - 1]): j-- } } // Only join when x and y are strings return (Array.isArray(a)) || (Array.isArray(b)) ? lcs : lcs.join('') } /** * Calculates the *length* of the longest common subsequence. * Also works with arrays. * * @see https://rosettacode.org/wiki/Longest_common_subsequence#JavaScript * @example * const lcsLength = StringBased.LongestCommonSubsequence.lcs('hello world!', 'world'); * // 5 * @param {string|Array} a a string * @param {string|Array} b another string * @returns {number} the length of longest common subsequence */ export function lcsLength (a, b) { // Make sure shorter string is the column string const m = a.length const n = b.length // Return now if one (or both) empty if (a.length === 0) { return 0 } if (b.length === 0) { return 0 } let i; let j; let row = []; let left; let diagonal; let latch const c = [] // Build the c-table for (j = 0; j < n; row[j++] = 0); for (i = 0; i < m; i++) { c[i] = row = [...row] for (diagonal = 0, j = 0; j < n; j++, diagonal = latch) { latch = row[j] if (a[i] === b[j]) { row[j] = diagonal + 1 } else { left = row[j - 1] || 0 if (left > row[j]) { row[j] = left } } } } i-- j-- // row[j] now contains the length of the lcs return row[j] } /** * Normalizes the result of lcsLength() by dividing by the longer string's * length. * * @param {string|Array} a a string * @param {string|Array} b another string * @returns {number} normalized length of longest common subsequence */ export function normalizedLcsLength (a, b) { const longerLength = Math.max(a.length, b.length) if (longerLength === 0) { return 0 } return lcsLength(a, b) / longerLength }