动态规划
特征一: 主问题的解包含了子问题的解;
特征二: 子问题出现重叠;
## 前缀动态规划:最长公共子序列(LCS)
- 问题描述:Z是序列X与Y的公共子序列,如果Z是X的子序列也是Y的子序列。
- Naive方法:
- 枚举X的每个子序列Z
- 检查Z是否为Y的子序列
- T(n)=O(n2m)
- 优化子结构:
- 设X=(x1, ..., xm)、Y=(y1, ..., yn)是两个序列, LCSXY=(z1, ..., zk)是X与Y的LCS,我们有:
- 如果xm=yn, 则zk=xm=yn, LCSXY = LCSXm-1Yn-1 + <xm=yn>, LCSXm-1Yn-1是Xm-1和Yn-1的LCS. (这个就是最有意思的地方,也就是说LCSXY的任何前缀 如LCSXY=(z1, ..., zk-1)必然是Xm-1和Yn-1的LCS(前缀))
- 如果xm≠yn,且zk≠xm,则LCSXY是Xm-1和Y的LCS,即 LCSXY = LCSXm-1Y
- 如果xm≠yn,且zk≠yn,则LCSXY是X与Yn-1的LCS,即 LCSXY = LCSXYn-1
- 子问题重叠性
- 方程:
- 自底向上计算:
- 伪代码
输入:X = (x1,x2,...,xm),Y = (y1,y2,...yn) 输出:Z = X与Y的最长公共子序列 C[0:m,0:n]: C[i,j]是Xi与Yj的LCS的长度 B[1:m,1:n]: B[i,j]是指针,指向计算C[i,j]时所选择的子问题的优化解所对应的C表的表项 LCS-length(X, Y) m←length(X);n←length(Y); For i←0 To m Do C[i,0]←0; For j←0 To n Do C[0,j]←0; For i←1 To m Do For j←1 To n Do If xi = yj Then C[i,j]←C[i-1,j-1]+1;B[i,j]←“↖”; Else If C[i-1,j]≥C[i,j-1] Then C[i,j]←C[i-1,j]; B[i,j]←“↑”; Else C[i,j]←C[i,j-1]; B[i,j]←“←”; Return C and B. Print-LCS(B, X, i, j) IF i=0 or j=0 THEN Return; IF B[i, j]=“↖” THEN Print-LCS(B, X, i-1, j-1); Print xi; ELSE If B[i, j]=“↑” THEN Print-LCS(B, X, i-1, j); ELSE Print-LCS(B, X, i, j-1). Print-LCS(B, X, length(X), length(Y)) 可打印出X与Y的LCS。
- 时间复杂度:
- 计算代价的时间:O(mn)
- 构造最优解的时间:O(m+n)
- 总时间复杂度为: O(mn)
- 空间复杂度:
- 使用数组C和B,需要空间O(mn)
特征一: 主问题的解包含了子问题的解;