LCS

求最长公共子序列
算法思想
1、定义dp [i][j]:表示字符串序列A的前i个字符组成的序列Ax和字符串序列B的前j个字符组成的序列By之间的最长 公共子序列L(i,j )的长度(m ,n分别为Ax和By的长度,i<=m,j<=n)
2、如果Ax [i] =By [j],那么Ax与By之间的最长公共子序列L( i,j )的最后一项一定是这个元素,
所以dp [i][j] = dp[i-1][j-1] + 1。
3、如Ax[i] != By[j],设LCS(i-1,j-1)是L( i -1, j-1 )最后一个元素,或者L(i-1,j-1)是空序列,
则t!= Ax[i]和t!=By[j]至少有一个不成立。
(1)当LCS(i-1,j-1) != Ax[i]时,dp[i][j]= dp[i-1][j];
(2)当LCS(i-1,j-1) != By[j]时,ap[i][j]= dp[i][j-1];
所以dp[i][j]= max ( dp[i-1][j],dp[i][j-1] )。
4、初始值为:dp[0][j] = dp[i][0] = 0.
5、题意要求求出任意一个最长公共子序列,这点要如何实现呢?
仍然考虑上面的递推式,L(i,j)的最后一个元素LCS( i,j )的来源有三种情况,定义数组flag[MAXN][MAXN]用以标记来的方向:
(1) dp[i][j] = dp[i-1][j-1] + 1,对应字符LCS( i-1,j-1)接上LCS( i,j),flag[i][j] = 1,表示从斜向上左方来;
(2) dp[i][j] = dp[i-1][j],对应字符LCS(i-1,j)接上LCS(i,j),flag[i][j] = 2,表示从上方过来;
(3) dp[i][j] = dp[i][j-1],对应字符LCS(I,j-1)接上LCS(i,j),flag[i][j] = 3,表示从左方过来。
我们在计算dp[i][j]时根据来源进行不同的标记,回溯就可以找到一个最长公共子序列。下图的答案是BCBA,长度4也符合。
在这里插入图片描述

#include<bits/stdc++.h>
char a[1010],b[1010];   //A,B两个串,注意下标是0开始存
int dp[1010][1010];     //最大公共子序列长度,注意下标是1开始存
int flag[1000][1000];   //标记来的方向,注意下标是1开始存
int main(){             //注意dp[i][j]是指a串[0~i-1]和b串[0~j-1]的LCS
    while(gets(a)){     //获得A串,非空继续
        gets(b);        //获B串
        memset(dp,0,sizeof(dp));//DP清空
        memset(flag,0,sizeof(flag));//标志清空
        int m = strlen(a);  //得A串长
        int n = strlen(b);  //得B串长
        int i,j;            //定义循环变量
        for(i = 1;i <= m;i++)       //扫一次A串
          for(j = 1;j <= n;j++){    //再扫一次B串
            if(a[i-1] == b[j-1]){   //两串的最后一位匹配(理清DP定义,与串下标差1!)
                dp[i][j] = dp[i-1][j-1] + 1;//然后DPIJ就可以在上一个DP基础上加一
                flag[i][j] = 1;     //然后打上方向1标记
            }//上一步(i-1,j-1),斜向左上
            else if(dp[i-1][j] > dp[i][j-1]){//如果不匹配,就直接取上方及左方格DP较大值
                dp[i][j] = dp[i-1][j];  //dp(i-1,j)大就取dp(i-1,j)
                flag[i][j] = 2;         //方向从2来
              }//上一步(i-1,j),上方
            else {                      //如果不匹配,就直接取上方及左方格DP较大值
                dp[i][j] = dp[i][j-1];  //dp(i,j-1)大就取dp(i,j-1)
                flag[i][j] = 3;         //方向从3来
              }//上一步(i,j-1),左方
          }
        int  k = 0;     //构造开始
        char c[1010];   //就是要输出的答案
        while(m > 0 && n > 0){      //从终点按标记的方向反方向往回走,因为DP与FLAG是从1起存的,所以>0
            if(flag[m][n] == 1){    //1号方向表示这个是新匹配
                c[k++] = a[m - 1];  //打入C数组
                m--; n--;           //左上角来,MN下标都减一
            }
            else if(flag[m][n] == 2)m--;//2号方向上面来,行减少
            else if(flag[m][n] == 3)n--;//3号方向左面来,列减少
        }
        for(i = k-1;i >= 0;i--)printf("%c",c[i]);//逆向输出
        printf("\n");
    }
    return 0;
}
/*
ABCBDAB
BDCABA

BDAB
*/

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值