class Solution(object):
def longestCommonSubsequence(self, text1, text2):
# dp[i][j]表示text1[0:i]和text2[0:j]之间的LCS长度
# 边界条件:当其中一个为空串时,LCS为0
dp = [[0] * (len(text2)+1) for _ in range(len(text1)+1)]
for i in range(1, len(text1)+1):
for j in range(1, len(text2)+1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i][j-1], dp[i-1][j])
return dp[len(text1)][len(text2)]
当输入为:"oxcpqrsvwf","shmtulqrypy"时,dp数组如下所示:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2]
[0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]
[0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]
[0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]
[0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]
外循环对text1的每个字符遍历,内循环对text2的每个字符遍历;
从dp数组输出可以看出,第一次字符相等出现在text1[3]和text2[9]字符p,但是这个p不在结果LCS中;
当外循环轮到text1[4]的字符q时,它遇到text2[6]的字符q,此时dp数组应该累加1;
但是每次dp数组累加1必须是这样的:
dp[i][j] = dp[i-1][j-1] + 1
这个等式表示:在text1[i-1]和text2[j-1]之前的text1和text2存在的LCS的基础上又多了一个字符,所以之前相同字符p不能被累加
当text1[i-1]和text2[j-1]不相等时,dp值就取目前长度下最大的dp值
dp[i][j] = max(dp[i][j-1], dp[i-1][j])
总结:
对于两个字符串的动态规划问题, ⼀般来说都是像本⽂⼀样定义 DP table,因为这样容易写出状态转移⽅程, dp[i][j] 的状态可以通过之前的状态推导出来: