最长共公共子序列和最长公共子串

原创 2015年07月07日 11:39:35

参考:http://www.cnblogs.com/zhangchaoyang/articles/2012070.html

最长公共子序列

最长公共子序列与最长公共子串的区别在于最长公共子序列不要求在原字符串中是连续的,比如ADE和ABCDE的最长公共子序列是ADE。

我们用动态规划的方法来思考这个问题如是求解。首先要找到状态转移方程:

符号约定,C1是S1的最右侧字符,C2是S2的最右侧字符,S1‘是从S1中去除C1的部分,S2’是从S2中去除C2的部分。

LCS(S1,S2)等于下列3项的最大者:

(1)LCS(S1,S2’)如果C1!=C2

(2)LCS(S1’,S2) 如果C1!=C2

(3) LCS(S1’,S2’)+C1 如果C1==C2;

边界终止条件:如果S1和S2都是空串,则结果也是空串。

以上理解:每次比较S1和S2时,有三种可能:
        1. S1 S2最后一个字符相等,那么直接将两者除去这个字符的公共子序列长度加一即可;
        2. S1 S2最后一个字符不等,那么又有两种情况:
            i. S1与S2去掉最后一个字符的序列相比, 得到子序列长度
            ii. S1去掉最后一个字符与S2相比,得到子序列长度
            最终选择i和ii中最大的长度作为S1与S2的公共子序列长度

下面我们同样要构建一个矩阵来存储动态规划过程中子问题的解。这个矩阵中的每个数字代表了该行和该列之前的LCS的长度。与上面刚刚分析出的状态转移相对应,矩阵中每个格子里的数字应该这么填,它等于以下3项的最大值:

(1)上面一个格子里的数字

(2)左边一个格子里的数字

(3)左上角格子里的数字+1(C1==C2时)

举个例子:

     G  C  T  A

   0  0  0  0  0

G  0  1  1  1  1

B  0  1  1  1  1

T  0  1  1  2  2

A 0  1  1  2  3

填写最后一个数字时,它应该是下面三个的最大者:

(1)上边的数字2

(2)左边的数字2

(3)左上角的数字2+1=3,因为此时C1==C2

所以最终结果是3。

在填写过程中我们还是记录下当前单元格的数字来自于哪个单元格,以方便最后我们回溯找出最长公共子串。由于每个格子可能来自于多个方向,如果想把所有可能的公共子序列全部输出的话,需要分别标记左、上、左上、左或左上四中可能,且回溯的时候如果遇到左上的情况,又需要递归回溯;所以为了方便以下程序中并没有把所有子序列都打印出来。

int lcs1(string a, string b)//最长共公共子序列
{
    int D[8][7];//D记录当前最长的公共子序列长度
    int S[7][6];//记录方向
    for (int i = 0; i <= a.length(); i++)
        D[i][0] = 0;
    for (int j = 0; j < b.length(); j++)
        D[0][j] = 0;
    //初始化第一行和第一列为0
    for (int i = 0; i < a.length(); i++)
    {
        for (int j = 0; j < b.length(); j++)
        {
            if (a[i] == b[j])
            {
                D[i + 1][j + 1] = D[i][j] + 1;
                S[i][j] = 0;//字符相等标记为0
            }
            else
            {
                D[i + 1][j + 1] = ((D[i][j + 1]>D[i + 1][j]) ? D[i][j + 1] : D[i + 1][j]);
                S[i][j] = ((D[i][j + 1] > D[i + 1][j]) ? -1 : 1);//从上传递来标记为-1,从左传递来标记1
            }
        }
    }
    cout << "公共子序列:";//如果想打印所有可能的子序列,需要单独用递归来打印,考虑左、上都有可能的情况
    int l = a.length() - 1;
    int r = b.length() - 1;
    while ((l >= 0) && (r>=0))
    {
        if (S[l][r] == 0)
        {
            cout << a[l] << " ";
            l--; r--;
        }
        else if (S[l][r] == -1)
            l--;
        else
            r--;
    }
    return D[6][5];
}

最长公共子串

找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。我们采用一个二维矩阵来记录中间的结果。这个二维矩阵怎么构造呢?直接举个例子吧:”bab”和”caba”(当然我们现在一眼就可以看出来最长公共子串是”ba”或”ab”)

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  1

a  0  1  0

我们看矩阵的斜对角线最长的那个就能找出最长公共子串。

不过在二维矩阵上找最长的由1组成的斜对角线也是件麻烦费时的事,下面改进:当要在矩阵是填1时让它等于其左上角元素加1。

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  2

a  0  2  0

这样矩阵中的最大元素就是 最长公共子串的长度。
输出子串的时候,找到矩阵中max所在的位置,然后循环max次即可得到字符串。

int lcs2(string a, string b)
{
    int D[8][7];
    for (int i = 0; i <= a.length(); i++)
        D[i][0] = 0;
    for (int i = 0; i <= b.length(); i++)
        D[0][i] = 0;
    //初始化
    int max = 0;//记当前最长子串的长度
    for (int i = 0; i < a.length(); i++)
    {
        for (int j = 0; j < b.length(); j++)
        {
            if (a[i] == b[j])
            {
                D[i+1][j+1] = D[i][j] + 1;//字符串相等就加一
                max = (D[i+1][j+1]>max) ? D[i+1][j+1] : max;
            }
            else
            {
                D[i+1][j+1] = 0;
            }
        }
    }
    for (int i = a.length(); i > 0; i--)//打印出所有的子串
    {
        for (int j = b.length(); j > 0; j--)
        {
            if (D[i][j] == max)
            {
                int t = i;
                for (int x = 0; x < max;x++)
                {
                    cout << a[t-1] << " ";
                    t--;
                }
                cout << endl;
            }
        }
    }
    return max;
}

poj之最长公共子序列和最长公共子串

题目:poj 1458   Common Subsequence Description A subsequence of a given sequence is the given sequen...
  • fangjian1204
  • fangjian1204
  • 2014年08月19日 18:31
  • 1621

最长公共子序列与最长公共子串(DP)

1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogsbelong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序...
  • u012102306
  • u012102306
  • 2016年11月16日 11:14
  • 3839

C++实现最长公共子序列和最长公共子串

转载自:点击打开链接 #include "stdafx.h" #include #include using namespace::std; int lcs(string str1, st...
  • chengonghao
  • chengonghao
  • 2016年07月14日 21:36
  • 5758

动态规划之最长公共子序列和最长公共字串,最大子序列和

最长公共子序列和最长公共字串
  • u010009623
  • u010009623
  • 2016年07月19日 15:23
  • 1631

动态规划:求最长公共子串/最长公共子序列

最长公共子序列和最长公共子串区别        最长公共子串(Longest Common Substring)与最长公共子序列(Longest Common Subsequence)的区别: 子串要...
  • u013074465
  • u013074465
  • 2015年04月30日 11:30
  • 11797

Python最长公共子串和最长公共子序列的实现

最长公共子串 (The Longest Common Substring)     LCS问题就是求两个字符串最长公共子串的问题。解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,...
  • wateryouyo
  • wateryouyo
  • 2016年03月17日 22:33
  • 7745

滴血总结(java版):最长公共子序列(子串)、最长公共回文子序列(子串)、最长公共前缀(后缀)

1,最长公共前缀问题 有点类似冒泡算法,每次都要找最小的串的长度,然后进行截取,代码如下 public String longestCommonPrefix(String[] strs) { ...
  • hll174
  • hll174
  • 2015年05月02日 19:22
  • 1184

三个或N个串的最长公共子序列

题意: 给出三个字符串,求出三个公共最长子串长度。 思路: 首先可以确定不同于两个串的公共最长,重复两次。因为C对于A -B 的最长公共子串会造成影响 A: abffgg B:affgg C:aa...
  • qq_33951440
  • qq_33951440
  • 2016年10月02日 15:57
  • 834

最长公共子序列与字符串的相似度问题

字符串的相似性:如果将一个串转换成为另一个串所需的操作数最少,那么可以说这两个串是相似的。另外一种权衡的方法是,寻换第三个串s3,如果s3都出现在s1和s2中,且出现的顺序相同,但不要求在s1和s2中...
  • jeason29
  • jeason29
  • 2014年11月30日 15:39
  • 400

最长公共子序列python实现

最长公共子序列是动态规划基本题目,下面按照动态规划基本步骤解出来。 1.找出最优解的性质,并刻划其结构特征 序列a共有m个元素,序列b共有n个元素,如果a[m-1]==b[n-1],那么a[:m]和b...
  • littlethunder
  • littlethunder
  • 2014年05月12日 17:05
  • 9616
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:最长共公共子序列和最长公共子串
举报原因:
原因补充:

(最多只允许输入30个字)