1143. 最长公共子序列
1、题目
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。
提示:
- 1 <= text1.length, text2.length <= 1000 ,text1 和 text2 仅由小写英文字符组成。
2、解题思路
看到这两个字符串的长度,很容易分析出时间复杂度应该是
o
(
n
2
)
o(n^2)
o(n2),又是字符串的题目,应该要马上想到有没有可能是二维dp的题。
我们假设
f
[
i
]
[
j
]
f[i][j]
f[i][j]是指前text1前i个字符和text2前j个字符的最长公共子序列。这时候需要考虑当前比对的字符
t
e
x
t
[
i
]
和
t
e
x
t
2
[
j
]
text[i]和text2[j]
text[i]和text2[j]是否相等。
如果相等的话,只要把text1前i-1个字符和text2前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−1][j−1]+1。
如果不相等的话,需要比较text1前i-1个字符和text2前j个字符和text1前i个字符和text2前j-1个字符的结果。
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
]
[
j
−
1
]
)
f[i][j]=max(f[i-1][j],f[i][j-1])
f[i][j]=max(f[i−1][j],f[i][j−1])
整理得状态转移方程:
f
[
i
]
[
j
]
=
{
f
[
i
−
1
]
[
j
−
1
]
+
1
t
e
x
t
1
[
i
]
=
t
e
x
t
2
[
j
]
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
]
[
j
−
1
]
)
t
e
x
t
1
[
i
]
≠
t
e
x
t
2
[
j
]
f[i][j]=\begin{cases} f[i-1][j-1]+1&text1[i]=text2[j]\\ max(f[i-1][j],f[i][j-1])& text1[i] \neq text2[j] \end{cases}
f[i][j]={f[i−1][j−1]+1max(f[i−1][j],f[i][j−1])text1[i]=text2[j]text1[i]=text2[j]
接下来考虑边界值,
f
[
0
]
[
j
]
,
f
[
i
]
[
0
]
=
{
1
t
e
x
t
1
[
i
]
=
t
e
x
t
2
[
j
]
0
t
e
x
t
1
[
i
]
≠
t
e
x
t
2
[
j
]
f[0][j],f[i][0]=\begin{cases} 1&text1[i]=text2[j]\\ 0& text1[i] \neq text2[j] \end{cases}
f[0][j],f[i][0]={10text1[i]=text2[j]text1[i]=text2[j]
到这题目已经算基本ok了。
容易发现,在这个过程中,我们只用到了 i - 1 行的结果和 i 行的结果,所以可以进一步将空间复杂度优化 o ( n ) o(n) o(n)。
最终代码:
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length(), temp1 = 0, temp2 = 0;
int[] dp = new int[m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
temp2 = dp[j];
if (text2.charAt(i) == text1.charAt(j))
dp[j] = temp1 + 1;
else if (j != 0)
dp[j] = dp[j - 1] > dp[j] ? dp[j - 1] : dp[j];
temp1 = j == m - 1 ? 0 : temp2;
}
}
return dp[m - 1];
}