最长公共子序列问题——简单版

什么是子序列

定义还是要搞清楚的
字符串s1s2···sn的子序列可以是si1si2···sim(这里的i1,i2,im并不一定要规律递增,只要保证是递增即可,例如2,4,6,9,或1,2,3,5皆可)。区分一个概念,字串是需要连续的,子序列只要保证下标递增即可。

思路

不用说,隔着屏幕都能嗅到动态规划的味道。
首先确定转移方程,我们用dp[m][n]这个二维数组存储每一个状态下的最大子序列的长度。
dp[i][j]的定义:字符串s1 ~ si处和字符串t1 ~ tj状态下的最大子序列的长度。 这句话最重要,dp[i][j]的定义不清晰,后面代码写起来很容易搞混。

注意,这里的ij可能有点搞。因为一般我们字符串的下标是从0开始的,而这里我们规定从1开始(一开始别那么绕嘛)。
接着dp[i][j]会怎么转移呢?
我们分两种情况讨论

  1. si==ti
    这种情况直接dp[i][j]=dp[i-1][j-1]+1即可,因为dp[i-1][j-1]存的就是上一种状态的最大值,因此直接+1即可
  2. si!=tj
    两个字符不相等,说明当前情况的最长子序列的长度依然维持上一种情况的最大值,所以我们要向之前的结果看,能到达这中情况的一共有两种老情况,分别是i不变,j向后退一步,和j不变,i向后退一步,所以表达式为
    dp[i][j]=max(dp[i-1][j],dp[i][j-1])

完整表达式为

if(s[i]==t[j]){
	dp[i][j]=dp[i-1][j-1]+1;
}else{
	dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

外层再套两个for循环即可。

AC代码

//像现在时间紧课业重的情况,我们不奢求去弄清楚每一种算法细微的区别
//我们的定位应该是找到一种能解决问题的方法,然后弄清楚这种方法
//LCS
#include<iostream>
#include<cstring>

using namespace std;

int m,n;
const int MAX_SIZE=1010;
char s[MAX_SIZE],t[MAX_SIZE];
int dp[MAX_SIZE][MAX_SIZE];

void solve();

int main(){
    scanf("%d %d\n",&m,&n);
    for(int i=1;i<=m;i++){
        scanf("%c",&s[i]);
    }
    getchar();
    for(int i=1;i<=n;i++){
        scanf("%c",&t[i]);
    }
    solve();
    return 0;
}

void solve(){
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(s[i]==t[j]){//当前字符相等,直接就是dp[][]+1,因为ij在字符串中和dp中表示的意义相同
                dp[i][j]=dp[i-1][j-1]+1;
            }else{//字符串不同的情况下
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//分别向前看即可
            }
        }
    }
    printf("%d",dp[m][n]);
}

字符串从0开始存储

//TODO:dp[i][j]定义为s0~si-1和t0~t-1的最长子序列的长度
#include<iostream>
#include<cstring>
#include<string>
using namespace std;

const int MAX_SIZE=1010;
int m,n;
string s,t;
int dp[MAX_SIZE][MAX_SIZE];

void solve();

int main(){
    cin >> m >> n;
    cin >> s >> t;
    solve();
    return 0;
}
//现在是字符串从0开始存储
void solve(){
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(s[i-1]==t[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;
            }else{
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    cout << dp[m][n];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值