LCS
思路:
明显的可以想到,状态转移方程为:
当x[i] == y[j]时,dp[i][j] = dp[i-1][j-1]+1
当x[i] != y[j]时,dp[i][j] = max(dp[i-1][j], dp[i][j-1])
这种方法可以结合下面的决策矩阵和代码加深理解(参考自https://blog.csdn.net/linraise/article/details/9104975
1 #include<iostream> 2 using namespace std; 3 #define M 7 4 #define N 6 5 int b[M + 1][N + 1] = {0}; //存方向 6 int c[M + 1][N + 1] = {0}; //存值 7 8 void Lcs_Length(char *X, char *Y) 9 { 10 int i, j; 11 for(i = 1; i <= M; i++) 12 c[i][0] = 0; 13 for(j = 0; j <= N; j++) 14 c[0][j] = 0; 15 for(i = 1; i <= M; i++) 16 { 17 for(j = 1; j <= N; j++) 18 { 19 if(X[i] == Y[j]) //比较两个字串对应位,如果相等,则=对角+1 20 { 21 c[i][j] = c[i - 1][j - 1] + 1; 22 b[i][j] = 1; //1代表↖ 23 } 24 else if(c[i - 1][j] >= c[i][j - 1]) //如果上面值>=下面值 25 { 26 c[i][j] = c[i - 1][j]; //c[i][j]=大值 27 b[i][j] = 2; //2代表↑ 28 } 29 else 30 { 31 c[i][j] = c[i][j - 1]; //赋的总是大值,箭头总是指向大值 32 b[i][j] = 3; //3代表← 33 } 34 } 35 } 36 } 37 void Print_Lcs(char *X, int i, int j) 38 { 39 if(i == 0 || j == 0) 40 return ; 41 if(b[i][j] == 1) 42 { 43 Print_Lcs(X, i - 1, j - 1); 44 cout << X[i] << ' '; //只需输出↖对应的值 45 } 46 else if(b[i][j] == 2) 47 Print_Lcs(X, i - 1, j); 48 else Print_Lcs(X, i, j - 1); 49 } 50 int main() 51 { 52 char X[M + 1] = {'0', 'A', 'B', 'C', 'B', 'D', 'A', 'B'}; 53 char Y[N + 1] = {'0', 'B', 'D', 'C', 'A', 'B', 'A'}; 54 Lcs_Length(X, Y); 55 Print_Lcs(X, M, N); 56 cout << endl; 57 for(int i = 0; i <= M; i++) 58 { 59 for(int j = 0; j <= N; j++) 60 { 61 cout << c[i][j] << ' '; 62 } 63 cout << endl; 64 } 65 cout << endl; 66 for(int i = 0; i <= M; i++) 67 { 68 for(int j = 0; j <= N; j++) 69 { 70 switch(b[i][j]) 71 { 72 case 0: 73 { 74 cout << b[i][j] << " "; 75 break; 76 } 77 case 1: 78 { 79 cout << '\\' << ' ' << ' '; 80 break; 81 } 82 case 2: 83 { 84 cout << '|' << ' ' << ' '; 85 break; 86 } 87 case 3: 88 { 89 cout << '-' << ' ' << ' '; 90 break; 91 } 92 } 93 } 94 cout << endl; 95 } 96 return 0; 97 }
进一步分析:
这种动态规划做法,用空间换取时间,对比暴力法,时间复杂度由O(m2n2)降低到了O(n2),那么有没有什么方法能进一步优化空间复杂度呢?答案是肯定的。分析上面的决策矩阵,发现每一行的值都是由上一行决定的,所以前面行的值是可以舍弃,不必保存的,这就可以使用滚动数组进行空间优化。
for(i = 1; i <= m; i++) { for(j = 1; j <= n; j++) { if(x[i] == y[j]) dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1; else if(dp[(i - 1) % 2][j] >= dp[i % 2][j - 1]) dp[i % 2][j] = dp[(i - 1) % 2][j]; else dp[i % 2][j] = dp[i % 2][j - 1]; } }
那么O(n2)的时间复杂度能继续优化吗?答案也是肯定的。做法是将LCS问题转换成O(nlogn)的LIS问题来求解,后续补充~
reference: