Skip to content

Commit d7614dc

Browse files
committed
feat: Longest Common Subsequence
1 parent 2d5b2b6 commit d7614dc

2 files changed

Lines changed: 242 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
title: LCR 095. 最长公共子序列
3+
toc: content
4+
tocDepth: 4
5+
---
6+
7+
## 题目 LCR 095. 最长公共子序列
8+
## 难度 🔥🔥中等
9+
给定两个字符串 `text1``text2`,返回这两个字符串的最长 `公共子序列` 的长度。如果不存在 `公共子序列` ,返回 `0`
10+
11+
一个字符串的 `子序列` 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
12+
13+
例如,`"ace"``"abcde"` 的子序列,但 `"aec" `不是 `"abcde"` 的子序列。
14+
两个字符串的 `公共子序列` 是这两个字符串所共同拥有的子序列。
15+
16+
#### 示例 1:
17+
18+
```bash
19+
输入:text1 = "abcde", text2 = "ace"
20+
输出:3
21+
解释:最长公共子序列是 "ace" ,它的长度为 3 。
22+
```
23+
24+
#### 示例 2:
25+
26+
```bash
27+
输入:text1 = "abc", text2 = "abc"
28+
输出:3
29+
解释:最长公共子序列是 "abc" ,它的长度为 3 。
30+
```
31+
32+
#### 示例 3:
33+
```bash
34+
输入:text1 = "abc", text2 = "def"
35+
输出:0
36+
解释:两个字符串没有公共子序列,返回 0 。
37+
```
38+
39+
#### 提示:
40+
41+
```bash
42+
- 1 <= text1.length, text2.length <= 1000
43+
- text1 和 text2 仅由小写英文字符组成。
44+
```
45+
46+
## 代码
47+
48+
首先,子序列问题本身就相对子串、子数组更困难一些,因为前者是不连续的序列,而后两者是连续的,就算穷举你都不一定会,更别说求解相关的算法问题了。
49+
50+
而且,子序列问题很可能涉及到两个字符串,比如` 最长公共子序列`,如果没有一定的处理经验,真的不容易想出来。所以本文就来扒一扒子序列问题的套路,其实就有两种模板,相关问题只要往这两种思路上想,十拿九稳。
51+
52+
一般来说,这类问题都是让你求一个最长子序列,因为最短子序列就是一个字符嘛,没啥可问的。`一旦涉及到子序列和最值,那几乎可以肯定,考察的是动态规划技巧`,时间复杂度一般都是 `O(n^2`)。
53+
54+
原因很简单,你想想一个字符串,它的子序列有多少种可能?起码是指数级的吧,这种情况下,不用动态规划技巧,还想怎么着?
55+
56+
既然要用动态规划,那就要定义 `dp` 数组,找状态转移关系。我们说的两种思路模板,就是` dp` 数组的定义思路。不同的问题可能需要不同的 `dp `数组定义来解决。
57+
58+
### 解法一 暴力算法
59+
最简单的暴力算法就是,把 `text1``text2` 的所有子序列都穷举出来,然后看看有没有公共的,然后在所有公共子序列里面再寻找一个长度最大的。
60+
61+
显然,这种思路的复杂度非常高,你要穷举出所有子序列,这个复杂度就是指数级的,**肯定不实际**。(`大家可以自己尝试`)
62+
63+
### 解法二 动态规划
64+
这个题目是典型的`二维动态规划问题`,就以本题而言,假设 `text1` 的长度为 `m``text2` 的长度为 `n`,那么,`dp` 数组的定义就是 `dp[i][j]` 表示 `text1` 的前 `i` 个字符和 `text2` 的前 `j` 个字符的最长公共子序列的长度。
65+
66+
`dp[i][j]` 的状态转移关系就是:,
67+
- 如果 `text1[i-1] == text2[j-1]`,那么 `dp[i][j] = dp[i-1][j-1] + 1`
68+
- 否则 `dp[i][j] = max(dp[i-1][j], dp[i][j-1])`
69+
70+
对于边界处理:
71+
-`i=0`时,text1[0,i] 为空,空字符串和任何字符串的最长公共子序列的长度都是 `0`,因此对任意` 0<=j<=n ,有 dp[0][j]=0`
72+
-`j=0`时,text2[0,j]为空,同理可得,对任意 `0<=i<=m ,有 dp[i][0]=0`
73+
74+
所以动态规划的边界情况是:当 `i= 0 || j = 0时, dp[i][j] = 0`
75+
76+
#### typescript
77+
```typescript
78+
function longestCommonSubsequence(text1: string, text2: string): number {
79+
/**
80+
* 动态规划
81+
*/
82+
83+
const m = text1.length , n = text2.length
84+
const dp = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
85+
86+
for(let i = 1; i < m+1;i++){
87+
for(let j = 1; j < n+1;j++){
88+
if(text1.charAt(i-1) === text2.charAt(j-1)){
89+
dp[i][j] = 1 + dp[i-1][j-1]
90+
}else{
91+
dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])
92+
}
93+
}
94+
}
95+
return dp[m][n]
96+
97+
};
98+
```
99+
100+
101+
#### javascript
102+
```javascript
103+
function longestCommonSubsequence(text1, text2) {
104+
const m = text1.length , n = text2.length
105+
const dp = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
106+
107+
for(let i = 1; i < m+1;i++){
108+
for(let j = 1; j < n+1;j++){
109+
if(text1.charAt(i-1) === text2.charAt(j-1)){
110+
dp[i][j] = 1 + dp[i-1][j-1]
111+
}else{
112+
dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])
113+
}
114+
}
115+
}
116+
return dp[m][n]
117+
};
118+
```
119+
120+
121+
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
title: 1143.最长公共子序列
3+
toc: content
4+
tocDepth: 4
5+
---
6+
7+
## 题目 最长公共子序列(Longest Common Subsequence 简称 LCS)
8+
## 难度 🔥🔥中等
9+
给定两个字符串 `text1``text2`,返回这两个字符串的最长 `公共子序列` 的长度。如果不存在 `公共子序列` ,返回 `0`
10+
11+
一个字符串的 `子序列` 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
12+
13+
例如,`"ace"``"abcde"` 的子序列,但 `"aec" `不是 `"abcde"` 的子序列。
14+
两个字符串的 `公共子序列` 是这两个字符串所共同拥有的子序列。
15+
16+
#### 示例 1:
17+
18+
```bash
19+
输入:text1 = "abcde", text2 = "ace"
20+
输出:3
21+
解释:最长公共子序列是 "ace" ,它的长度为 3 。
22+
```
23+
24+
#### 示例 2:
25+
26+
```bash
27+
输入:text1 = "abc", text2 = "abc"
28+
输出:3
29+
解释:最长公共子序列是 "abc" ,它的长度为 3 。
30+
```
31+
32+
#### 示例 3:
33+
```bash
34+
输入:text1 = "abc", text2 = "def"
35+
输出:0
36+
解释:两个字符串没有公共子序列,返回 0 。
37+
```
38+
39+
#### 提示:
40+
41+
```bash
42+
- 1 <= text1.length, text2.length <= 1000
43+
- text1 和 text2 仅由小写英文字符组成。
44+
```
45+
46+
## 代码
47+
48+
首先,子序列问题本身就相对子串、子数组更困难一些,因为前者是不连续的序列,而后两者是连续的,就算穷举你都不一定会,更别说求解相关的算法问题了。
49+
50+
而且,子序列问题很可能涉及到两个字符串,比如` 最长公共子序列`,如果没有一定的处理经验,真的不容易想出来。所以本文就来扒一扒子序列问题的套路,其实就有两种模板,相关问题只要往这两种思路上想,十拿九稳。
51+
52+
一般来说,这类问题都是让你求一个最长子序列,因为最短子序列就是一个字符嘛,没啥可问的。`一旦涉及到子序列和最值,那几乎可以肯定,考察的是动态规划技巧`,时间复杂度一般都是 `O(n^2`)。
53+
54+
原因很简单,你想想一个字符串,它的子序列有多少种可能?起码是指数级的吧,这种情况下,不用动态规划技巧,还想怎么着?
55+
56+
既然要用动态规划,那就要定义 `dp` 数组,找状态转移关系。我们说的两种思路模板,就是` dp` 数组的定义思路。不同的问题可能需要不同的 `dp `数组定义来解决。
57+
58+
### 解法一 暴力算法
59+
最简单的暴力算法就是,把 `text1``text2` 的所有子序列都穷举出来,然后看看有没有公共的,然后在所有公共子序列里面再寻找一个长度最大的。
60+
61+
显然,这种思路的复杂度非常高,你要穷举出所有子序列,这个复杂度就是指数级的,**肯定不实际**。(`大家可以自己尝试`)
62+
63+
### 解法二 动态规划
64+
这个题目是典型的`二维动态规划问题`,就以本题而言,假设 `text1` 的长度为 `m``text2` 的长度为 `n`,那么,`dp` 数组的定义就是 `dp[i][j]` 表示 `text1` 的前 `i` 个字符和 `text2` 的前 `j` 个字符的最长公共子序列的长度。
65+
66+
`dp[i][j]` 的状态转移关系就是:,
67+
- 如果 `text1[i-1] == text2[j-1]`,那么 `dp[i][j] = dp[i-1][j-1] + 1`
68+
- 否则 `dp[i][j] = max(dp[i-1][j], dp[i][j-1])`
69+
70+
对于边界处理:
71+
-`i=0`时,text1[0,i] 为空,空字符串和任何字符串的最长公共子序列的长度都是 `0`,因此对任意` 0<=j<=n ,有 dp[0][j]=0`
72+
-`j=0`时,text2[0,j]为空,同理可得,对任意 `0<=i<=m ,有 dp[i][0]=0`
73+
74+
所以动态规划的边界情况是:当 `i= 0 || j = 0时, dp[i][j] = 0`
75+
76+
#### typescript
77+
```typescript
78+
function longestCommonSubsequence(text1: string, text2: string): number {
79+
/**
80+
* 动态规划
81+
*/
82+
83+
const m = text1.length , n = text2.length
84+
const dp = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
85+
86+
for(let i = 1; i < m+1;i++){
87+
for(let j = 1; j < n+1;j++){
88+
if(text1.charAt(i-1) === text2.charAt(j-1)){
89+
dp[i][j] = 1 + dp[i-1][j-1]
90+
}else{
91+
dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])
92+
}
93+
}
94+
}
95+
return dp[m][n]
96+
97+
};
98+
```
99+
100+
101+
#### javascript
102+
```javascript
103+
function longestCommonSubsequence(text1, text2) {
104+
const m = text1.length , n = text2.length
105+
const dp = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
106+
107+
for(let i = 1; i < m+1;i++){
108+
for(let j = 1; j < n+1;j++){
109+
if(text1.charAt(i-1) === text2.charAt(j-1)){
110+
dp[i][j] = 1 + dp[i-1][j-1]
111+
}else{
112+
dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])
113+
}
114+
}
115+
}
116+
return dp[m][n]
117+
};
118+
```
119+
120+
121+

0 commit comments

Comments
 (0)