zoukankan      html  css  js  c++  java
  • 【动态规划】最长公共子序列问题

    题目描述:

    给定两个字符串s1s2……sn和t1t2……tn。求出这两个字符串最长的公共子序列的长度。字符串s1s2……sn的子序列指可以表示为si1si2……sim(i1<i2<……<im)的序列。(n>=1,m<=1000)

    输入:

    n=4

    m=4

    s="abcd"

    t="becd"

    输出:

    3

    分析:

    阶段就是n=0,1,2,3,4……时,m=0,1,2,3……1000时;状态就是对应字符相等还是不相等。

    看该题是否符合,每个阶段的最优状态可以从之前的某个阶段的某个或某些状态得到而不管之前的状态是如何得到的(最优子结构和无后效性)。

    用输入输出的栗子证一下:

    当n=4,m=4时,S3为‘d’,T3为‘d’,S3==T3,则此阶段对应的最长公共子序列(LCS)为n=3,m=3时的LCS加1。

    当n=2,m=2时,S1为'b',T1为'e',S1!=T3,则此阶段对应的LCS为,n=2,m=1阶段的LCS与n=1,m=2阶段的LCS的最大值。

    记忆化搜索代码:

    #include <iostream>
    #include <string>
    #include <string.h>
    #define MAX_N 1001
    #define MAX_M 1001
    using namespace std;
    int n,m;
    string s,t;
    int dp[MAX_N][MAX_M];
    int rec(int ni,int mi){
    if(dp[ni][mi]>=0){
        return dp[ni][mi];
    }
    int res;
    //当s和t的字符串长度为1时,它们参考的前一个LCS设为0
    if(ni==0||mi==0){
        res=0;
    }
    else if((ni>0&&ni<=n)&&(mi>0&&mi<=m)){
        if(s[ni-1]==t[mi-1]){
            res=rec(ni-1,mi-1)+1;
        }
        else if(s[ni-1]!=t[mi-1]){
            res=max(rec(ni-1,mi),rec(ni,mi-1));
        }
    }
    dp[ni][mi]=res;
    return res;
    }
    int main()
    {
        cin>>n>>m;
        cin>>s>>t;
        memset(dp,-1,sizeof(dp));
        cout<<rec(n,m)<<endl;
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                cout<<dp[i][j];
                if(j<m){
                    cout<<' ';
                }
                else if(j==m){
                    cout<<endl;
                }
            }
        }
        return 0;
    }

    根据记忆化搜索推到出递推公式,本题比较简单也可以直接写出来:

    当m或n等于0,dp[n][0]=0,dp[0][m]=0;

    当Si与Ti相等时,dp[n][m]=dp[n-1][m-1]+1;

    当Si与Ti不相等时,dp[n][m]=max(dp[n-1][m],dp[n][m-1]);

    举个不相等的栗子:

    "abcdef"和"defihg",'f'和'g'不相等,则考虑"abcdef"和"defih"其LCS为3。

    动态规划的代码:

    #include <iostream>
    #include <string>
    #define MAX_N 1001
    #define MAX_M 1001
    using namespace std;
    int dp[MAX_N][MAX_M];
    int main()
    {
        int n,m;
        string s,t;
    
        cin>>n>>m;
        cin>>s>>t;
        //递推公式中的n用i+1表示,m用j+1表示
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(s[i]==t[j]){
                    dp[i+1][j+1]=dp[i][j]+1;
                }
                else if(s[i]!=t[j]){
                    dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
                }
            }
        }
        cout<<dp[n][m]<<endl;
        return 0;
    }
    祝你早日攒够失望,然后开始新的生活。
  • 相关阅读:
    【Eolinker使用】接口测试--如何解决接口重定向
    ExtJS按钮
    Redis-消费模式
    Redis笔记教程
    C++中this指针的用法
    C — 对C语言的认识
    你还在迷茫什么
    2019-2020-1 20199324《Linux内核原理与分析》第四周作业
    2019-2020-1 20199324《Linux内核原理与分析》第三周作业
    2019-2020-1 20199324《Linux内核原理与分析》第二周作业
  • 原文地址:https://www.cnblogs.com/LuRenJiang/p/7183480.html
Copyright © 2011-2022 走看看