This commit is contained in:
Oleksii Trekhleb 2018-04-26 07:03:59 +03:00
parent a672474acc
commit 9bef8de6b1
4 changed files with 96 additions and 1 deletions

View File

@ -44,7 +44,7 @@
* [KnuthMorrisPratt Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/knuth-morris-pratt) - substring search * [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 * [Rabin Karp Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/rabin-karp) - substring search
* [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-subsequnce) (LCS) * [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-subsequnce) (LCS)
* longest common substring * [Longest Common Substring](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-substring)
* **Search** * **Search**
* [Binary Search](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/search/binary-search) * [Binary Search](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/search/binary-search)
* **Sorting** * **Sorting**
@ -74,6 +74,7 @@
* **Dynamic Programming** * **Dynamic Programming**
* [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences * [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
* [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-subsequnce) (LCS) * [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-subsequnce) (LCS)
* [Longest Common Substring](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-substring)
* Increasing subsequence * Increasing subsequence
* Knapsack problem * Knapsack problem
* Maximum subarray * Maximum subarray

View File

@ -0,0 +1,24 @@
# Longest Common Substring Problem
The longest common substring problem is to find the longest string
(or strings) that is a substring (or are substrings) of two or more
strings.
## Example
The longest common substring of the strings `ABABC`, `BABCA` and
`ABCBA` is string `ABC` of length 3. Other common substrings are
`A`, `AB`, `B`, `BA`, `BC` and `C`.
```
ABABC
|||
BABCA
|||
ABCBA
```
## References
- [Wikipedia](https://en.wikipedia.org/wiki/Longest_common_substring_problem)
- [YouTube](https://www.youtube.com/watch?v=BysNXJHzCEs)

View File

@ -0,0 +1,11 @@
import longestCommonSubstring from '../longestCommonSubstring';
describe('longestCommonSubstring', () => {
it('should find longest common substring between two strings', () => {
expect(longestCommonSubstring('', '')).toBe('');
expect(longestCommonSubstring('ABC', '')).toBe('');
expect(longestCommonSubstring('', 'ABC')).toBe('');
expect(longestCommonSubstring('ABABC', 'BABCA')).toBe('BABC');
expect(longestCommonSubstring('BABCA', 'ABCBA')).toBe('ABC');
});
});

View File

@ -0,0 +1,59 @@
/**
* @param {string} s1
* @param {string} s2
* @return {string}
*/
export default function longestCommonSubstring(s1, s2) {
// Init the matrix of all substring lengths to use Dynamic Programming approach.
const substringMatrix = Array(s2.length + 1).fill(null).map(() => {
return Array(s1.length + 1).fill(null);
});
// Fill the first row and first column with zeros to provide initial values.
for (let columnIndex = 0; columnIndex <= s1.length; columnIndex += 1) {
substringMatrix[0][columnIndex] = 0;
}
for (let rowIndex = 0; rowIndex <= s2.length; rowIndex += 1) {
substringMatrix[rowIndex][0] = 0;
}
// Build the matrix of all substring lengths to use Dynamic Programming approach.
let longestSubstringLength = 0;
let longestSubstringColumn = 0;
let longestSubstringRow = 0;
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]) {
substringMatrix[rowIndex][columnIndex] = substringMatrix[rowIndex - 1][columnIndex - 1] + 1;
} else {
substringMatrix[rowIndex][columnIndex] = 0;
}
// Try to find the biggest length of all common substring lengths
// and to memorize its last character position (indices)
if (substringMatrix[rowIndex][columnIndex] > longestSubstringLength) {
longestSubstringLength = substringMatrix[rowIndex][columnIndex];
longestSubstringColumn = columnIndex;
longestSubstringRow = rowIndex;
}
}
}
if (longestSubstringLength === 0) {
// Longest common substring has not been found.
return '';
}
// Detect the longest substring from the matrix.
let longestSubstring = '';
while (substringMatrix[longestSubstringRow][longestSubstringColumn] > 0) {
longestSubstring = s1[longestSubstringColumn - 1] + longestSubstring;
longestSubstringRow -= 1;
longestSubstringColumn -= 1;
}
return longestSubstring;
}