题目地址:
https://leetcode.com/problems/longest-common-subsequence/
求两个字符串 s 1 s_1 s1和 s 2 s_2 s2的最长公共子序列长度。
设
s
1
s_1
s1的长度是
n
n
n,
s
2
s_2
s2的长度是
m
m
m,思路是动态规划。设
f
(
i
,
j
)
f(i,j)
f(i,j)代表
s
1
[
0
,
1
,
.
.
.
,
i
]
s1[0,1,...,i]
s1[0,1,...,i]和
s
2
[
0
,
1
,
.
.
.
,
j
]
s2[0,1,...,j]
s2[0,1,...,j]的最长公共子序列的长度。可以按照这个最长公共子序列是否包含
s
1
[
i
]
s1[i]
s1[i]和
s
2
[
j
]
s2[j]
s2[j]来分类讨论,那么有四种情况(这四种情况包含了所有情况,但并不互斥。由于这里是求最长值,所以不互斥并没有影响):
1、这个最长公共子序列不包含
s
1
[
i
]
s1[i]
s1[i]和
s
2
[
j
]
s2[j]
s2[j];
2、这个最长公共子序列包含
s
1
[
i
]
s1[i]
s1[i],但不包含
s
2
[
j
]
s2[j]
s2[j];
3、这个最长公共子序列不包含
s
1
[
i
]
s1[i]
s1[i],但包含
s
2
[
j
]
s2[j]
s2[j];
4、这个最长公共子序列既包含
s
1
[
i
]
s1[i]
s1[i],又包含
s
2
[
j
]
s2[j]
s2[j];
很显然
f
[
i
]
[
j
]
f[i][j]
f[i][j]是这四种情况的最大值(容易看出来这四种情况并不是互斥的,比如第一种情况就被包含在了第二和三种情况中了,但这不影响我们求最大值)。其中,第一种情况就是
f
[
i
−
1
]
[
j
−
1
]
f[i-1][j-1]
f[i−1][j−1],第二三种情况分别被包含在
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1]和
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j]的情况中(意思是如果有满足第二种的最长公共子序列,那么其长度必然不超过
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1]),第四种情况取决于
s
1
[
i
]
s1[i]
s1[i]和
s
2
[
j
]
s2[j]
s2[j]是否相等。如果相等,那么
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
+
1
f[i][j]=f[i-1][j-1]+1
f[i][j]=f[i−1][j−1]+1,否则的话,
f
[
i
]
[
j
]
f[i][j]
f[i][j]就等于前三种情况的最大值。不管怎样,要求
f
[
i
]
[
j
]
f[i][j]
f[i][j],本质上就是求
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1],
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j]和
f
[
i
−
1
]
[
j
−
1
]
+
1
f[i-1][j-1]+1
f[i−1][j−1]+1的最大值。这三个都是规模更小的问题。代码如下:
class Solution {
public:
int longestCommonSubsequence(string s1, string s2) {
int n = s1.size(), m = s2.size();
int f[n + 1][m + 1];
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (s1[i - 1] == s2[j - 1]) f[i][j] = 1 + f[i - 1][j - 1];
else f[i][j] = max(f[i][j - 1], f[i - 1][j]);
return f[n][m];
}
};
时空复杂度 O ( m n ) O(mn) O(mn)。