This commit is contained in:
Oleksii Trekhleb 2018-04-25 15:36:05 +03:00
parent 0e46d3e164
commit bf24ea3316
4 changed files with 100 additions and 2 deletions

View File

@ -39,11 +39,11 @@
* [Least Common Multiple](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/least-common-multiple) (LCM)
* [FisherYates Shuffle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/fisher-yates) - random permutation of a finite sequence
* **String**
* [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences (DP approach)
* [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences (dynamic programming approach)
* [Hamming Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/hamming-distance) - number of positions at which the symbols are different
* [KnuthMorrisPratt Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/knuth-morris-pratt) - substring search
* [Rabin Karp Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/rabin-karp) - substring search
* Longest common subsequence
* [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-subsequnce) (LCS)
* longest common substring
* **Search**
* [Binary Search](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/search/binary-search)

View File

@ -0,0 +1,25 @@
# Longest common subsequence problem
The longest common subsequence (LCS) problem is the problem of finding
the longest subsequence common to all sequences in a set of sequences
(often just two sequences). It differs from the longest common substring
problem: unlike substrings, subsequences are not required to occupy
consecutive positions within the original sequences.
## Application
The longest common subsequence problem is a classic computer science
problem, the basis of data comparison programs such as the diff utility,
and has applications in bioinformatics. It is also widely used by
revision control systems such as Git for reconciling multiple changes
made to a revision-controlled collection of files.
## Example
- LCS for input Sequences `ABCDGH` and `AEDFHR` is `ADH` of length 3.
- LCS for input Sequences `AGGTAB` and `GXTXAYB` is `GTAB` of length 4.
## References
- [Wikipedia](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem)
- [YouTube](https://www.youtube.com/watch?v=NnD96abizww)

View File

@ -0,0 +1,13 @@
import longestCommonSubsequnce from '../longestCommonSubsequnce';
describe('longestCommonSubsequnce', () => {
it('should find longest common subsequence for two strings', () => {
expect(longestCommonSubsequnce('', '')).toBe('');
expect(longestCommonSubsequnce('', 'ABC')).toBe('');
expect(longestCommonSubsequnce('ABC', '')).toBe('');
expect(longestCommonSubsequnce('ABC', 'DEFG')).toBe('');
expect(longestCommonSubsequnce('ABCDGH', 'AEDFHR')).toBe('ADH');
expect(longestCommonSubsequnce('AGGTAB', 'GXTXAYB')).toBe('GTAB');
expect(longestCommonSubsequnce('ABCDAF', 'ACBCF')).toBe('ABCF');
});
});

View File

@ -0,0 +1,60 @@
/**
* @param {string} s1
* @param {string} s2
* @return {string}
*/
export default function longestCommonSubsequnce(s1, s2) {
// Init LCS matrix.
const lcsMatrix = Array(s2.length + 1).fill(null).map(() => Array(s1.length + 1).fill(null));
// Fill first row with zeros.
for (let columnIndex = 0; columnIndex <= s1.length; columnIndex += 1) {
lcsMatrix[0][columnIndex] = 0;
}
// Fill first column with zeros.
for (let rowIndex = 0; rowIndex <= s2.length; rowIndex += 1) {
lcsMatrix[rowIndex][0] = 0;
}
// Fill rest of the column that correspond to each of two strings.
for (let rowIndex = 1; rowIndex <= s2.length; rowIndex += 1) {
for (let columnIndex = 1; columnIndex <= s1.length; columnIndex += 1) {
if (s1[columnIndex - 1] === s2[rowIndex - 1]) {
lcsMatrix[rowIndex][columnIndex] = lcsMatrix[rowIndex - 1][columnIndex - 1] + 1;
} else {
lcsMatrix[rowIndex][columnIndex] = Math.max(
lcsMatrix[rowIndex - 1][columnIndex],
lcsMatrix[rowIndex][columnIndex - 1],
);
}
}
}
// Calculate LCS based on LCS matrix.
if (!lcsMatrix[s2.length][s1.length]) {
// If the length of largest common string is zero then return empty string.
return '';
}
let lcs = '';
let columnIndex = s1.length;
let rowIndex = s2.length;
while (columnIndex > 0 || rowIndex > 0) {
if (s1[columnIndex - 1] === s2[rowIndex - 1]) {
// Move by diagonal left-top.
lcs = s1[columnIndex - 1] + lcs;
columnIndex -= 1;
rowIndex -= 1;
} else if (lcsMatrix[rowIndex][columnIndex] === lcsMatrix[rowIndex][columnIndex - 1]) {
// Move left.
columnIndex -= 1;
} else {
// Move up.
rowIndex -= 1;
}
}
return lcs;
}