最长公共子序列

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

动态规划方法:

1、序列str1和序列str2
 
  ·长度分别为m和n;
  ·创建1个二维数组L[m.n];
    ·初始化L数组内容为0
    ·m和n分别从0开始,m++,n++循环:
       - 如果str1[m] == str2[n],则L[m,n] = L[m - 1, n -1] + 1;
       - 如果str1[m] != str2[n],则L[m,n] = max{L[m,n - 1],L[m - 1, n]}
    ·最后从L[m,n]中的数字一定是最大的,且这个数字就是最长公共子序列的长度
    ·从数组L中找出一个最长的公共子序列

   2、从数组L中查找一个最长的公共子序列

   i和j分别从m,n开始,递减循环直到i = 0,j = 0。其中,m和n分别为两个串的长度。
  ·如果str1[i] == str2[j],则将str[i]字符插入到子序列内,i--,j--;
  ·如果str1[i] != str[j],则比较L[i,j-1]与L[i-1,j],L[i,j-1]大,则j--,否则i--;(如果相等,则任选一个)

举个例子:

       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。

在填写过程中我们还是记录下当前单元格的数字来自于哪个单元格,以方便最后我们回溯找出最长公共子串。有时候左上、左、上三者中有多个同时达到最大,那么任取其中之一,但是在整个过程中你必须遵循固定的优先标准。在我的代码中优先级别是左上>左>上。

下图给出了回溯法找出LCS的过程:

代码:



#include<iostream>

#include<cstring>

#include<stack>

#include<utility>

#define LEFTUP 0

#define LEFT 1

#define UP 2

using namespace std;

int Max(int a,int b,int c,int *max){            //找最大者时a的优先级别最高,c的最低.最大值保存在*max中

    int res=0;          //res记录来自于哪个单元格

    *max=a;

    if(b>*max){

        *max=b;

        res=1;

    }

    if(c>*max){

        *max=c;

        res=2;

    }

    return res;

}

//调用此函数时请注意把较长的字符串赋给str1,这主要是为了在回溯最长子序列时节省时间。如果没有把较长的字符串赋给str1不影响程序的正确执行。

string LCS(const string &str1,const string &str2){

    int xlen=str1.size();               //横向长度

    int ylen=str2.size();               //纵向长度

    if(xlen==0||ylen==0)                //str1和str2中只要有一个为空,则返回空

        return "";

    pair<int,int> arr[ylen+1][xlen+1];    //构造pair二维数组,first记录数据,second记录来源

    for(int i=0;i<=xlen;i++)         //首行清0

        arr[0][i].first=0;

    for(int j=0;j<=ylen;j++)         //首列清0

        arr[j][0].first=0;

    for(int i=1;i<=ylen;i++){

        char s=str2.at(i-1);

        for(int j=1;j<=xlen;j++){

            int leftup=arr[i-1][j-1].first;

            int left=arr[i][j-1].first;

            int up=arr[i-1][j].first;

            if(str1.at(j-1)==s)         //C1==C2

                leftup++;

            int max;

            arr[i][j].second=Max(leftup,left,up,&arr[i][j].first);

//          cout<<arr[i][j].first<<arr[i][j].second<<" ";

        }

//      cout<<endl;

    }       /*矩阵构造完毕*/

    //回溯找出最长公共子序列

    stack<int> st;

    int i=ylen,j=xlen;

    while(i>=0&&j>=0){

        if(arr[i][j].second==LEFTUP){

            if(arr[i][j].first==arr[i-1][j-1].first+1)

                st.push(i);

            --i;

            --j;

        }

        else if(arr[i][j].second==LEFT){

            --j;

        }

        else if(arr[i][j].second==UP){

            --i;

        }

    }

    string res="";

    while(!st.empty()){

        int index=st.top()-1;

        res.append(str2.substr(index,1));

        st.pop();

    }

    return res;

}

int main(){

    string str1="GCCCTAGCG";

    string str2="GCGCAATG";

    string lcs=LCS(str1,str2);

    cout<<lcs<<endl;

    return 0;

} 


下面给一个Java版本







1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31 


public static <E> List<E> longestCommonSubsequence(E[] s1,E[] s2){

        int[][] num=new int[s1.length+1][s2.length+1];

        for(int i=1;i<s1.length+1;i++){

            for(int j=1;j<s2.length+1;j++){

                if(s1[i-1].equals(s2[j-1])){

                    num[i][j]=1+num[i-1][j-1];

                }

                else{

                    num[i][j]=Math.max(num[i-1][j],num[i][j-1]);

                }

            }

        }

        System.out.println("lenght of LCS= "+num[s1.length][s2.length]);

        int s1position=s1.length,s2position=s2.length;

        List<E> result=new LinkedList<E>();

        while(s1position>0 && s2position>0){

            if(s1[s1position-1].equals(s2[s2position-1])){

                result.add(s1[s1position-1]);

                s1position--;

                s2position--;

            }

            else if(num[s1position][s2position-1]>=num[s1position-1][s2position]){

                s2position--;

            }

            else{

                s1position--;

            }

        }

        Collections.reverse(result);

        return result;

    } 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值