码迷,mamicode.com
首页 > 其他好文 > 详细

dp求解各种子串子序列

时间:2020-02-02 23:19:38      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:def   概念   忽略   scanf   algorithm   convert   orm   return   ons   

注:dp可能并不是求解该这些问题的最优算法,这里只是做一个dp 算法的简介。

概念

定义:假设现有一个 string = abcdefghijklmn

最长连续子串:要求在原序列中连续,比如 str = abcdfghijklm都是valid substring

最长连续子序列:相对顺序在原序列中不变即可;比如 str = afghdfkn等等都是valid subsequence

说完了上面的定义;下面来说一说怎么用dp求解最长连续子串和最长连续子序列;既然用到了dp 的方法求解,就要找出相关的状态转移方程。

最长上升子序列

#include <bits/stdc++.h>

using namespace std;
const int maxn=3e4+10;
int a[maxn],b[maxn],n;
int dp[maxn],ans;     //dp[i]表示前i个的最长上升子序列

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)   scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)   scanf("%d",&b[i]);
    for(int i=1;i<=n;++i){
        dp[i]=1;
        for(int j=1;j<i;++j){
            if(a[j]<a[i]&&b[j]<b[i])    dp[i]=max(dp[i],dp[j]+1);
            ans=max(ans,dp[i]);
        }
    }
    printf("%d\n",ans);
    system("pause");
}

最长连续子串

递推方程:

技术图片

int f[maxn][maxn];
int solve(char *x, char *y) 
{
   int ans=0;
   int lenx=strlen(x);
   int leny=strlen(y);
   for (int i=0;i<lenx;i++) 
   {
       for (int j=0;j<leny;j++){
           if (x[i]!=y[j])      f[i][j]=0;
           else if(x[i]==y[j])  f[i][j]=f[i-1][j-1]+1;
           ans=max(ans,f[i][j]);
       }
    }
    return ans;
}

最长公共子序列

状态转移方程:

技术图片

模板(花里胡哨的dp请忽略,网上版本,也可成二维数组):

int dp[maxn][maxn];         //a[1]~a[i]与b[1]~b[j]的最长公共子序列
int solve(int n,int m)
{
    for (int i=0;i<=n;i++)  dp[i][0] = 0;
    for (int i=0;i<=m;i++)  dp[0][i] = 0;
    for (int i=1;i<=n;i++){     //n,m分别为两个数组的长度
        for (int j=1;j<=m;j++){
            if (a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1;
            else    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
    return dp[n][m];
}

最长公共上升子序列

dp[i][j]的含义同最长公共子序列;

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
const int maxn=3e3+10;
int a[maxn],b[maxn];
int dp[maxn][maxn];     //表示a[1]~a[i]与b[1]~b[j]且以b[j]为结尾的最长上升公共子序列

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)   scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)   scanf("%d",&b[i]);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            if(a[i]==b[j]){
                dp[i][j]=1;
                for(int k=1;k<j;++k){
                    if(b[k]<a[i])   dp[i][j]=max(dp[i][j],dp[i-1][k]+1);
                }
            }
            else dp[i][j]=dp[i-1][j];
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i)   ans=max(ans,dp[n][i]);
    printf("%d\n",ans);
    system("pause");
}

上面是未优化前的代码;起代码复杂度未O(n^3);其中的k循环可以被优化;优化后时间复杂度为O(n^2),看代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
const int maxn=3e3+10;
int a[maxn],b[maxn];
int dp[maxn][maxn]; //表示a[1]~a[i]与b[1]~b[j]且以b[j]为结尾的最长上升公共子序列

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)   scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)   scanf("%d",&b[i]);
    for(int i=1;i<=n;++i){
        int maxv=1;
        for(int j=1;j<=n;++j){
            dp[i][j]=dp[i-1][j];
            if(a[i]==b[j])  dp[i][j]=max(dp[i][j],maxv);
            if(b[j]<a[i])   maxv=max(maxv,dp[i][j]+1);
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i)   ans=max(ans,dp[n][i]);
    printf("%d\n",ans);
    system("pause");
}

dp求解各种子串子序列

标签:def   概念   忽略   scanf   algorithm   convert   orm   return   ons   

原文地址:https://www.cnblogs.com/StungYep/p/12254070.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!