动态规划
最长上升子序列是单序列动态规划的典例,最长公共子序列则是双序列动态规划的典例。这次仍然会仔细分析状态表示和转移关系,并且给出最优子结构性质的说明,以后的帖子中就重点关注状态定义和转移关系,而不再细说最优子结构,重叠子问题了
由于涉及两个序列s,t,因此dp表也需要二维了,此处我们定义dp(i,j)为序列s的子段s[0..i]和序列t的子段t[0..j]中的最长公共子序列长度,这里提到的“子段”为原序列中一些连续的元素构成的序列。这里情况比较复杂,给出下面的图表来分析:
由此可以得出递推关系:
dp(i,j)={dp(i−1,j−1)+1,s[i]=t[j]max{dp(i−1,j),dp(i,j−1)},s[i]≠t[j]
最优子结构的说明比较长,借用一下别人的图:
为了方便计算,序列的下标可以从1开始。如果序列s长度为m,序列t长度为n,最终结果就是dp(m,n)
C++ 代码
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int m, n;
int** dp = new int*[m + 1];
for (int i = 0; i <= n; i++) {
dp[i] = new int[n + 1](); //空括号代表默认赋值(0)
}
string str1, str2;
cin >> str1 >> str2;
str1 = ' ' + str1; //两字符串都向右偏移1,让有效下标从1开始
str2 = ' ' + str2;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i] == str2[j]) { //按照递推公式推
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
cout << dp[m][n] << endl;
return 0;
}