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:

https://blog.csdn.net/linraise/article/details/9104975

原文地址:https://www.cnblogs.com/friend-A/p/10298157.html