最长公共子序列与最长公共子串--动态规划

最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。

最长公共子序列

问题:对于母串X=<x1,x2,⋯,xm>,Y=<y1,y2,⋯,yn>,求LCS与最长公共子串。
动态规划解决:
假设Z=<z1,z2,⋯,zk>是X与Y的LCS, 我们观察到

  • 如果xm=yn,则zk=xm=yn,有Zk−1Xm−1Yn−1的LCS;
  • 如果xm≠yn,则ZkXmYn−1的LCS,或者是Xm−1Yn的LCS。

因此,求解LCS的问题则变成递归求解的两个子问题。但是,上述的递归求解的办法中,重复的子问题多,效率低下。改进的办法——用空间换时间,用数组保存中间状态,方便后面的计算。这就是动态规划(DP)的核心思想了。

用二维数组c[i][j]记录串x1x2⋯xiy1y2⋯yj的LCS长度,则可得到状态转移方程:
这里写图片描述
代码实现:

#include<iostream>
#include<string.h>
#include<algorithm>
#define MAX 1005
using namespace std;
int temp[MAX][MAX];
int LCS(string &s1,string &s2){
    memset(temp,0,MAX*sizeof(int));
    int len1 = s1.length();
    int len2 = s2.length();
    for(int i = 0; i < len1; i++){
        for(int j = 0; j < len2; j++){
            if(s1[i] == s2[j]){
                temp[i+1][j+1] = temp[i][j]+1;//从下标(1,1)开始存储
            }else{
                temp[i+1][j+1] = max(temp[i+1][j],temp[i][j+1]);
            }
        }
    }
    return temp[len1][len2];    
} 

代码解释:
其实就是一个填充二维数组记录状态的过程。
假设s1='blog',s2='long'
则记录状态的二维数组temp为:

index(s1,s2)01234
00----
1-0000
2-1111
3-1222
4-1223

解释下temp:纵向为s1字符串的下标,横向为s2字符串的下标
第三行:用s1(‘blog’)的b去匹配逐一匹配s2(‘long’),因为s2没有b,所以记录下的公共子序列都为0.
第四行:用s1(‘blog’)的l去匹配逐一匹配s2(‘long’),第一个匹配到了l,所以可以确定目前的最长公共子序列长度为1,然后接着匹配,o,n,g都与l不匹配,所以就从上方(temp[i][j+1])和左侧(temp[i+1][j])选取最大者。(原因:上方表示b匹配到的最长公共子序列,左侧表示bl目前匹配到的最长公共子序列)
剩下的原理都相同。

最长公共子串

考虑到子串的连续性,将二维数组c[i,j]c[i,j]用来记录具有这样特点的子串——结尾为母串x1x2⋯xiy1y2⋯yj的结尾——的长度。
状态方程为:(子串只需在意之前的序列长度就行了)
这里写图片描述
代码:

#include<iostream>
#include<string.h>
#include<algorithm>
#define MAX 1005
using namespace std;
int temp[MAX][MAX];
int result = 0;;
int LCS(string &s1,string &s2){
    memset(temp,0,MAX*sizeof(int));
    int len1 = s1.length();
    int len2 = s2.length();
    for(int i = 0; i < len1; i++){
        for(int j = 0; j < len2; j++){
            if(s1[i] == s2[j]){
                temp[i+1][j+1] = temp[i][j]+1;
                result = max(temp[i+1][j+1],result);//记录下最大值 
            }else{
                temp[i+1][j+1] = 0;
            }
        }
    }
    return result;  
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值