一、问题定义
|---------|-------------|---------------------------------|
| 问题 | 输入 | 输出 |
| 最长重复子数组 | 两个数组/字符串A,B | 最长的连续子数组,同时出现在A和B中 |
| 最长公共子序列 | 两个序列A,B | 最长的子序列(元素相对顺序一致,可不连续),同时出现在A和B中 |
| 编辑距离 | 两个字符串A,B | 将A转换成B所需的最少插入,删除,替换操作次数 |
二、动态规划解法对比
1.状态定义
- 重复子数组:dp[i][j]表示以A[i-1]和B[j-1]结尾的最长公共子串的长度
- 公共子序列:dp[i][j]表示A[0..i-1]和B[0..j-1]的最长公共子序列的长度
- 编辑距离:dp[i][j]表示将A[0..i-1] 转换成 B[0..j-1]所需的最少操作次数
2.状态转移方程
设m= len(A),n = len(B),下标从1开始(即dp[i][j] 对应A[i-1]与B[j-1])
|-------|-------------------------------------|---------------------------------------------------------------------|
| 问题 | 当A[i-1]==B[j-1] | 当A[i-1]!=B[j-1] |
| 重复子数组 | dp[i][j]=dp[i-1][i-1]+1 | dp[i][j]=0(连续中断) |
| 公共子序列 | dp[i][j]=dp[i-1][j-1]+1 | dp[i][j]=max(dp[i-1][j],dp[i][j-1]) |
| 编辑距离 | dp[i][j]=dp[i-1][j-1](无需操作) | dp[i][j]=min(dp[i-1][j], dp[i][j-1],dp[i-1][j-1])+1 |
3.初始化
- 重复子数组:dp[0][j]=0,dp[i][0]=0(空串匹配长度为0)
- 公共子序列:同上,全0
- 编辑距离:dp[i][0]=i (将A前i个字符删除),dp[0][j]=j(插入j个字符)
4.最终答案
- 重复子数组:max(dp[i][j]) ,即DP表中最大值(因为最佳子串不一定在末尾结束)
- 公共子序列:dp[m][n]
- 编辑距离:dp[m][n]
三、异同点
相同点
- 动态规划框架:都基于二维表,逐行逐列递推
- 时间复杂度:均为O(mn),空间均可优化到O(min(m,n))
- 子结构:都利用了"前缀"的最优解来构造更长前缀的最优解
不同点
| 维度 | 重复子数组 | 公共子序列 | 编辑距离 |
|---|---|---|---|
| 连续性要求 | 必须连续 | 不要求连续 | 不直接涉及连续,但通过操作调整 |
| 不等时的行为 | 置 0(重新开始) | 取左/上的最大值(跳过) | 取三种操作的最小值+1 |
| 初始化 | 全 0 | 全 0 | 第一行、第一列为 i, j |
| 最终答案 | 全局最大值 | 右下角 | 右下角 |
| DP 值的含义 | 以当前位置结尾的最长匹配长度 | 前缀的最长公共子序列长度 | 前缀的最小编辑代价 |
| 空间优化难度 | 简单(仅需上一行) | 中等(需两行) | 中等(需两行,并保存左上角) |
四、举例说明
设 A = "abcde", B = "abfce"。
1. 最长重复子数组(公共子串)
-
匹配的连续段:
"ab"(长度 2)、"c"(长度 1)、"e"(长度 1)。 -
DP 表(只写部分):
text
复制
下载
a b f c e a 1 0 0 0 0 b 0 2 0 0 0 c 0 0 0 1 0 d 0 0 0 0 0 e 0 0 0 0 1最大值 = 2 →
"ab"。
2. 最长公共子序列
-
子序列:
"abce"(长度 4)。 -
DP 表(右下角):
text
复制
下载
a b f c e a 1 1 1 1 1 b 1 2 2 2 2 c 1 2 2 3 3 d 1 2 2 3 3 e 1 2 2 3 4答案 = 4。
3. 编辑距离("abcde" → "abfce")
-
最少操作:将
'c'替换为'f',然后删除'd'?实际最优:把'c'改成'f'(1 次),再把'd'删除(1 次),共 2 次。 -
DP 表右下角 = 2。
五、代码示例
1.最长重复子数组
python
def findLength(A, B):
m, n = len(A), len(B)
dp = [[0] * (n + 1) for _ in range(m + 1)]
ans = 0
for i in range(1, m + 1):
for j in range(1, n + 1):
if A[i-1] == B[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
ans = max(ans, dp[i][j])
return ans
2.最长公共子序列
python
def longestCommonSubsequence(A, B):
m, n = len(A), len(B)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if A[i-1] == B[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
3.编辑距离
python
def minDistance(A, B):
m, n = len(A), len(B)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
dp[i][0] = i
for j in range(1, n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if A[i-1] == B[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
return dp[m][n]