Skip to content

Commit e81b48e

Browse files
committed
Add longest increase and shortest common
1 parent 845b1e0 commit e81b48e

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Chuỗi Con Tăng Dần Dài Nhất
2+
3+
Bài toán về chuỗi con tăng dần dài nhất là tìm chuỗi con của một chuỗi đã cho, trong đó các phần tử của chuỗi con được sắp xếp theo thứ tự, từ thấp nhất đến cao nhất, và đấy là chuỗi con dài nhất có thể. Chuỗi con này không nhất thiết phải liền kề hoặc duy nhất.
4+
5+
## Độ phức tạp
6+
7+
Chuỗi con tăng dần dài nhất có thời gian giải là `O(n log n)`, với `n` là độ dài chuỗi đầu vào.
8+
9+
Phương pháp quy hoạch động có độ phức tạp là `O(n*n)`.
10+
11+
## Ví dụ
12+
13+
Ta có 16 số hạng đầu tiên của chuỗi nhị phân Van der Corput:
14+
15+
```text
16+
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
17+
```
18+
19+
có chuỗi con tăng dần dài nhất là:
20+
21+
```text
22+
0, 2, 6, 9, 11, 15.
23+
```
24+
25+
Chuỗi con tăng dần dài nhất trong ví dụ này không phải là duy nhất, ví dụ:
26+
27+
```text
28+
0, 4, 6, 9, 11, 15 or
29+
0, 2, 6, 9, 13, 15 or
30+
0, 4, 6, 9, 13, 15
31+
```
32+
33+
là các chuỗi con tăng dần khác có độ dài bằng nhau trong cùng một chuỗi đầu vào.
34+
35+
## Liên kết
36+
37+
- [Wikipedia](https://en.wikipedia.org/wiki/Longest_increasing_subsequence)
38+
- [Dynamic Programming Approach on YouTube](https://www.youtube.com/watch?v=CE2b_-XfVDk&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import dpLongestIncreasingSubsequence from '../dpLongestIncreasingSubsequence';
2+
3+
describe('dpLongestIncreasingSubsequence', () => {
4+
it('should find longest increasing subsequence length', () => {
5+
// Should be:
6+
// 9 or
7+
// 8 or
8+
// 7 or
9+
// 6 or
10+
// ...
11+
expect(dpLongestIncreasingSubsequence([
12+
9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
13+
])).toBe(1);
14+
15+
// Should be:
16+
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
17+
expect(dpLongestIncreasingSubsequence([
18+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
19+
])).toBe(10);
20+
21+
// Should be:
22+
// -1, 0, 2, 3
23+
expect(dpLongestIncreasingSubsequence([
24+
3, 4, -1, 0, 6, 2, 3,
25+
])).toBe(4);
26+
27+
// Should be:
28+
// 0, 2, 6, 9, 11, 15 or
29+
// 0, 4, 6, 9, 11, 15 or
30+
// 0, 2, 6, 9, 13, 15 or
31+
// 0, 4, 6, 9, 13, 15
32+
expect(dpLongestIncreasingSubsequence([
33+
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15,
34+
])).toBe(6);
35+
});
36+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Phương pháp quy hoạch động tìm chuỗi con tăng dần dài nhất.
3+
* Độ phức tạp: O(n * n)
4+
*
5+
* @param {number[]} sequence
6+
* @return {number}
7+
*/
8+
export default function dpLongestIncreasingSubsequence(sequence) {
9+
// Tạo mảng chứa độ dài của chuỗi con tăng dần dài nhất và điền 1
10+
// có nghĩa là mỗi phần tử là một chuỗi con tăng dần tối thiếu.
11+
const lengthsArray = Array(sequence.length).fill(1);
12+
13+
let previousElementIndex = 0;
14+
let currentElementIndex = 1;
15+
16+
while (currentElementIndex < sequence.length) {
17+
if (sequence[previousElementIndex] < sequence[currentElementIndex]) {
18+
// Nếu phần tử hiện tại lớn hơn phần tử trước đó thì nó là một
19+
// phần của chuỗi con tăng dần có độ dài lớn hơn chuỗi con tăng
20+
// dần trước đó.
21+
const newLength = lengthsArray[previousElementIndex] + 1;
22+
if (newLength > lengthsArray[currentElementIndex]) {
23+
// Chỉ tăng nếu phần tử trước đó cho ta chuỗi con có độ
24+
// dài lớn hơn ta đang có ở phần tử hiện tại.
25+
lengthsArray[currentElementIndex] = newLength;
26+
}
27+
}
28+
29+
// Di chuyển chỉ số phần tử trước đó sang phải.
30+
previousElementIndex += 1;
31+
32+
// Nếu chỉ số phần tử trước đó bằng với chỉ số phần tử hiện tại thì hãy dịch
33+
// chuyển phần tử hiện tại sang phải và đặt lại chỉ số phần tử trước đó về 0.
34+
if (previousElementIndex === currentElementIndex) {
35+
currentElementIndex += 1;
36+
previousElementIndex = 0;
37+
}
38+
}
39+
40+
// Tìm phần tử lớn nhất trong lengthsArray.
41+
// Con số này là độ dài lớn nhất của chuỗi con tăng dần.
42+
let longestIncreasingLength = 0;
43+
44+
for (let i = 0; i < lengthsArray.length; i += 1) {
45+
if (lengthsArray[i] > longestIncreasingLength) {
46+
longestIncreasingLength = lengthsArray[i];
47+
}
48+
}
49+
50+
return longestIncreasingLength;
51+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Chuỗi Chung Ngắn Nhất
2+
3+
Chuỗi chung ngắn nhất (SCS) của hai chuỗi `X``Y` là chuỗi ngắn nhất chứa `X``Y` như là chuỗi con.
4+
5+
Nói đơn giản, là cho hai chuỗi `str1``str2`, hãy tìm chuỗi ngắn nhất chứa cả hai chuỗi `str1``str2`.
6+
7+
Bài toán này có liên quan chặc chẽ với tìm chuỗi con chung dài nhất (LCS).
8+
9+
## Ví dụ
10+
11+
```
12+
Input: str1 = "geek", str2 = "eke"
13+
Output: "geeke"
14+
Input: str1 = "AGGTAB", str2 = "GXTXAYB"
15+
Output: "AGXGTXAYB"
16+
```
17+
18+
## Liên kết
19+
20+
- [GeeksForGeeks](https://www.geeksforgeeks.org/shortest-common-supersequence/)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import shortestCommonSupersequence from '../shortestCommonSupersequence';
2+
3+
describe('shortestCommonSupersequence', () => {
4+
it('should find shortest common supersequence of two sequences', () => {
5+
// LCS (longest common subsequence) is empty
6+
expect(shortestCommonSupersequence(
7+
['A', 'B', 'C'],
8+
['D', 'E', 'F'],
9+
)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
10+
11+
// LCS (longest common subsequence) is "EE"
12+
expect(shortestCommonSupersequence(
13+
['G', 'E', 'E', 'K'],
14+
['E', 'K', 'E'],
15+
)).toEqual(['G', 'E', 'K', 'E', 'K']);
16+
17+
// LCS (longest common subsequence) is "GTAB"
18+
expect(shortestCommonSupersequence(
19+
['A', 'G', 'G', 'T', 'A', 'B'],
20+
['G', 'X', 'T', 'X', 'A', 'Y', 'B'],
21+
)).toEqual(['A', 'G', 'G', 'X', 'T', 'X', 'A', 'Y', 'B']);
22+
23+
// LCS (longest common subsequence) is "BCBA".
24+
expect(shortestCommonSupersequence(
25+
['A', 'B', 'C', 'B', 'D', 'A', 'B'],
26+
['B', 'D', 'C', 'A', 'B', 'A'],
27+
)).toEqual(['A', 'B', 'D', 'C', 'A', 'B', 'D', 'A', 'B']);
28+
29+
// LCS (longest common subsequence) is "BDABA".
30+
expect(shortestCommonSupersequence(
31+
['B', 'D', 'C', 'A', 'B', 'A'],
32+
['A', 'B', 'C', 'B', 'D', 'A', 'B', 'A', 'C'],
33+
)).toEqual(['A', 'B', 'C', 'B', 'D', 'C', 'A', 'B', 'A', 'C']);
34+
});
35+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import longestCommonSubsequence from '../longest-common-subsequence/longestCommonSubsequence';
2+
3+
/**
4+
* @param {string[]} set1
5+
* @param {string[]} set2
6+
* @return {string[]}
7+
*/
8+
9+
export default function shortestCommonSupersequence(set1, set2) {
10+
// Tìm chuỗi con chung dài nhất của hai chuỗi.
11+
const lcs = longestCommonSubsequence(set1, set2);
12+
13+
// Nếu LCS rỗng thì chuỗi chung ngắn nhất sẽ chỉ là nối hai chuỗi lại.
14+
if (lcs.length === 1 && lcs[0] === '') {
15+
return set1.concat(set2);
16+
}
17+
18+
// Không thì ta sẽ thêm phần tử set1 và set2 vào theo thứ tự
19+
// trước/trong/sau của LCS.
20+
let supersequence = [];
21+
22+
let setIndex1 = 0;
23+
let setIndex2 = 0;
24+
let lcsIndex = 0;
25+
let setOnHold1 = false;
26+
let setOnHold2 = false;
27+
28+
while (lcsIndex < lcs.length) {
29+
// Thêm phần tử của set1 vào chuỗi supersequence theo đúng thứ tự.
30+
if (setIndex1 < set1.length) {
31+
if (!setOnHold1 && set1[setIndex1] !== lcs[lcsIndex]) {
32+
supersequence.push(set1[setIndex1]);
33+
setIndex1 += 1;
34+
} else {
35+
setOnHold1 = true;
36+
}
37+
}
38+
39+
// Thêm phần tử của set2 vào chuỗi supersequence theo đúng thứ tự.
40+
if (setIndex2 < set2.length) {
41+
if (!setOnHold2 && set2[setIndex2] !== lcs[lcsIndex]) {
42+
supersequence.push(set2[setIndex2]);
43+
setIndex2 += 1;
44+
} else {
45+
setOnHold2 = true;
46+
}
47+
}
48+
49+
// Thêm phần tử của LCS vào chuỗi supersequence theo đúng thứ tự.
50+
if (setOnHold1 && setOnHold2) {
51+
supersequence.push(lcs[lcsIndex]);
52+
lcsIndex += 1;
53+
setIndex1 += 1;
54+
setIndex2 += 1;
55+
setOnHold1 = false;
56+
setOnHold2 = false;
57+
}
58+
}
59+
60+
// Đính kèm phần thừa của set1.
61+
if (setIndex1 < set1.length) {
62+
supersequence = supersequence.concat(set1.slice(setIndex1));
63+
}
64+
65+
// Đính kèm phần thừa của set2.
66+
if (setIndex2 < set2.length) {
67+
supersequence = supersequence.concat(set2.slice(setIndex2));
68+
}
69+
70+
return supersequence;
71+
}

0 commit comments

Comments
 (0)