• 子序列问题


    最长公共子序列


    例:求两个字符串最长公共子序列长度。
    如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)

  • 相关阅读:
    CSS 按类查看的常用样式属性
    html5前端准备资料
    编译和链接一些错误和警告
    [转]assert()函数用法总结
    [转]C++ 函数模板特化导致的多重定义链接错误
    [转]退出线程的几种方法
    [总结]编程中遇到的vc提示的一些警告
    [转]__declspec(dllexport) 和 __declspec(dllimport)
    [转]PROCESS_INFOMATION
    [转载] STARTUPINFO结构体
  • 原文地址:https://www.cnblogs.com/mrclr/p/8119816.html
Copyright © 2020-2023  润新知