子序列问题

最长公共子序列


例:求两个字符串最长公共子序列长度。
如a[] = {"abcedf"}, b[] = {"abtrenf},则最长公共子序列为abef,长度为4

伪代码:

1 for(i...a[]的长度)
2     for(j...b[]的长度)
3     {
4         dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
5             if(a[i] == b[j])
6                 dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
7     }
8     输出dp[a[]的长度][b[]的长度]

代码

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6 const int maxn = 250;
 7 int dp[maxn][maxn];
 8 char a[maxn], b[maxn];
 9 int main()
10 {
11     scanf("%s%s", a + 1, b + 1);    
12     /*a + 1意思是从a[1]开始读入,而并非从a[0],这么做是
13     为了后面dp时dp[i][j - 1]不越界 。但相应的后面循环就
14     要从 i = 1到 i = la,而不是到 i = la - 1*/
15     int la = strlen(a + 1), lb = strlen(b + 1);
16     for(int i = 1; i <= la; ++i)
17     {
18         for(int j = 1; j <= lb; ++j)
19         {
20             dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
21             if(a[i] == b[j])
22                 dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
23         }
24     }
25     printf("%d\n", dp[la][lb]);
26     return 0;
27 }

这是子序列的一个最基本的问题,从这个问题可以衍生出很多相关的子序列问题。

 

最长回文子序列

例:有一个字符串,求最少删去几个字符,使它成为一个回文字符串。

思路:转化为求最长公共子序列问题:将这个字符串倒序存入另一个字符串,求这两个字符串的最长公共子序列

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6 const int maxn = 250;
 7 int n ,dp[maxn][maxn];
 8 char a[maxn], b[maxn];
 9 int main()
10 {
11     scanf("%d", &n);
12     scanf("%s", a + 1);
13     for(int i = 1; i <= n; ++i)
14         b[i] = a[n - i + 1];
15     for(int i = 1; i <= n; ++i)
16     {
17         for(int j = 1; j <= n; ++j)
18         {
19             dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
20             if(a[i] == b[j])
21                 dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
22         }
23     }
24     printf("%d\n", dp[n][n]);
25     return 0;
26 }

 

最长单调子序列

例:有一个整形数组,求他的最长递增(递减)子序列。

思路:转化为求最长公共子序列问题:将这个数组排序,存到另一个数组中,求这两个数组的最长公共子序列。
这个方法空间复杂度为O(2n),可以改进为O(n)。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 using namespace std;
 6 const int maxn = 10005;
 7 int a[maxn], b[maxn];
 8 int dp[maxn][maxn], n;
 9 int main()
10 {
11     scanf("%d", &n);
12     for(int i = 1; i <= n; ++i)
13     {
14         scanf("%d", &a[i]);
15         b[i] = a[i];
16     }
17     sort(b + 1, b + n + 1);
18     for(int i = 1; i <= n; ++i)
19     {
20         for(int j = 1; j <= n; ++j)
21         {
22             dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
23             if(a[i] == b[j])
24                 dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
25         }
26     }
27     printf("%d\n", dp[n][n]);
28     return 0;
29 }

改进:设dp[i]表示以a[i]为终点的最长上升子序列的长度,则当a[j] < a[i],且j < i时,dp[i] = dp[j] + 1.
所以dp方程:
dp[i] = max(dp[i], dp[j] + 1)

posted @ 2017-12-26 20:14  mrclr  阅读(304)  评论(0编辑  收藏  举报