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

来源:互联网 发布:淘宝智能版首页尺寸 编辑:程序博客网 时间:2024/06/09 18:37

最长公共子串(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 1005using 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) 0 1 2 3 4 0 0 - - - - 1 - 0 0 0 0 2 - 1 1 1 1 3 - 1 2 2 2 4 - 1 2 2 3

解释下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 1005using 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