动态规划
特征一: 主问题的解包含了子问题的解;
特征二: 子问题出现重叠;
## 前缀动态规划:最长公共子序列(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)
特征一: 主问题的解包含了子问题的解;

特征二: 子问题出现重叠;

