现在的位置: 首页 > 综合 > 正文

LCS/最长公共子序列算法分析

2019年11月04日 ⁄ 综合 ⁄ 共 8020字 ⁄ 字号 评论关闭

                                         最长公共子序列

一、最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(LongestCommon Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。

    例如:X(A,B,C,B,D,A,B)

          Y(B,D,C,A,B,A)

那么最长公共子序列就是:B,C,B,A

二、算法设计:

方法一、穷举法  

    解最长公共子序列问题时最容易想到的算法是穷举搜索法,即对X的每一个子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列,并且在检查过程中选出最长的公共子序列。X和Y的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此,X共有2m个不同子序列(Y亦如此,如为2^n),从而穷举搜索法需要指数时间(2^m
* 2^n)。

方法二、用动态规划方法解决

最长公共子序列的结构:

设X = { x1 ,... , xm },Y = { y1 , ... , yn }及它们的最长子序列Z= { z1 , ... , zk }则:

1、若 xm = yn , 则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列

2、若 xm != yn ,且 zk != xm, 则 Z 是 X[m-1] 和 Y 的最长公共子序列

3、若 xm != yn , 且 zk != yn, 则 Z 是 Y[n-1] 和 X 的最长公共子序列

子问题的递归结构:

1、当 i = 0 , j = 0 时 , c[i][j] = 0

2、当 i , j > 0 ; x[i-1] = y[j-1] 时 , c[i][j] = c[i-1][j-1] + 1

3、当 i , j > 0 ; xi[i-1]!= y[j-1] 时 , c[i][j] = max { c[i][j-1] , c[i-1][j] }

算法:


打印(回溯法):

   由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m + n)。


算法详细示例图:

代码一:

/*
Right:greenday
*/
#include<stdio.h>
#include<string.h>
//打印最长序列 
void Display(int bin[][], char x[], int i, int j) {  
    if(i == 0 || j == 0)  
        return;     
    if(b[i][j] == 1)  
    {  
        Display(bin, x, i-1, j-1);  
        System.out.print(x[i] + " ");  
    }  
    else if(bin[i][j] == 0)  
    {  
        Display(bin, x, i-1, j);  
    }  
    else if(bin[i][j] == -1)  
    {  
        Display(bin, x, i, j-1);  
    }  
} 
int main(){
	int num;
	scanf("%d",&num);
	while(num--){
	   char tring1[1001];
	   char tring2[1001];
	   int slenth[1001][1001];
	   int lujing[1001][1001];
	   scanf("%s",tring1);
	   scanf("%s",tring2);
	   
	   //gets(tring1);
	   //gets(tring2);
	   int len1=strlen(tring1);//LCS
	   int len2=strlen(tring2);
	   //初始数组点值为1,至少长度为1 
	   for(int i=0;i<=len1;i++)
	      slenth[i][0]=0;	 
       for(int j=0;j<=len2;j++)
	      slenth[0][j]=0;	 
	   for(int i=1;i<=len1;i++){
		 for(int j=1;j<=len2;j++){
		 	//判断字母的大小 
		 	if(tring1[i-1]==tring2[j-1]){
		 	  //动态规划 
		 	  slenth[i][j]=slenth[i-1][j-1]+1; 
		 	  lujing[i][j]=1;
		 	}
		 	else if(slenth[i][j-1]>slenth[i-1][j]){
		 	  slenth[i][j]=slenth[i][j-1]; 
		 	  lujing[i][j]=-1;
		 	}
		 	else{
		 	  slenth[i][j]=slenth[i-1][j]; 
		 	  lujing[i][j]=0;
		 	}
		 } 
	   } 
	   printf("%d\n",slenth[len1][len2]);//LCS
	   
	}
	return 0;
}

代码二:

#include "stdafx.h"
#include "string.h"
#include <iostream>
using namespace std;

// directions of LCS generation
enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp};

void LCS_Print(int **LCS_direction, char* pStr1, char* pStr2, size_t row, size_t col);
// Get the length of two strings' LCSs, and print one of the LCSs
// Input: pStr1         - the first string
//        pStr2         - the second string
// Output: the length of two strings' LCSs
int LCS(char* pStr1, char* pStr2)
{
	if(!pStr1 || !pStr2)
		return 0;

	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);
	if(!length1 || !length2)
		return 0;

	size_t i, j;

	// initiate the length matrix
	int **LCS_length;
	LCS_length = (int**)(new int[length1]);
	for(i = 0; i < length1; ++ i)
		LCS_length[i] = (int*)new int[length2];

	for(i = 0; i < length1; ++ i)
		for(j = 0; j < length2; ++ j)
			LCS_length[i][j] = 0;

	// initiate the direction matrix
	int **LCS_direction;
	LCS_direction = (int**)(new int[length1]);
	for( i = 0; i < length1; ++ i)
		LCS_direction[i] = (int*)new int[length2];

	for(i = 0; i < length1; ++ i)
		for(j = 0; j < length2; ++ j)
			LCS_direction[i][j] = kInit;

	for(i = 0; i < length1; ++ i)
	{
		for(j = 0; j < length2; ++ j)
		{
			//之前此处的代码有问题,现在订正如下:
			if(i == 0 || j == 0) 
			{ 
				if(pStr1[i] == pStr2[j]) 
				{ 
					LCS_length[i][j] = 1; 
					LCS_direction[i][j] = kLeftUp; 
				} 
				else 
				{ 
					if(i > 0) 
					{ 
						LCS_length[i][j] = LCS_length[i - 1][j]; 
						LCS_direction[i][j] = kUp; 
					} 
					if(j > 0) 
					{ 
						LCS_length[i][j] = LCS_length[i][j - 1]; 
						LCS_direction[i][j] = kLeft; 
					} 
				} 
			}
			// a char of LCS is found, 
			// it comes from the left up entry in the direction matrix
			else if(pStr1[i] == pStr2[j])
			{
				LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1;
				LCS_direction[i][j] = kLeftUp;
			}
			// it comes from the up entry in the direction matrix
			else if(LCS_length[i - 1][j] > LCS_length[i][j - 1])
			{
				LCS_length[i][j] = LCS_length[i - 1][j];
				LCS_direction[i][j] = kUp;
			}
			// it comes from the left entry in the direction matrix
			else
			{
				LCS_length[i][j] = LCS_length[i][j - 1];
				LCS_direction[i][j] = kLeft;
			}
		}
	}
	LCS_Print(LCS_direction, pStr1, pStr2, length1 - 1, length2 - 1); //调用下面的LCS_Pring 打印出所求子串。
	return LCS_length[length1 - 1][length2 - 1];                      //返回长度。
}

// Print a LCS for two strings
// Input: LCS_direction - a 2d matrix which records the direction of 
//                        LCS generation
//        pStr1         - the first string
//        pStr2         - the second string
//        row           - the row index in the matrix LCS_direction
//        col           - the column index in the matrix LCS_direction
void LCS_Print(int **LCS_direction, char* pStr1, char* pStr2, size_t row, size_t col)
{
	if(pStr1 == NULL || pStr2 == NULL)
		return;

	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);

	if(length1 == 0 || length2 == 0 || !(row < length1 && col < length2))
		return;

	// kLeftUp implies a char in the LCS is found
	if(LCS_direction[row][col] == kLeftUp)
	{
		if(row > 0 && col > 0)
			LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col - 1);

		// print the char
		printf("%c", pStr1[row]);
	}
	else if(LCS_direction[row][col] == kLeft)
	{
		// move to the left entry in the direction matrix
		if(col > 0)
			LCS_Print(LCS_direction, pStr1, pStr2, row, col - 1);
	}
	else if(LCS_direction[row][col] == kUp)
	{
		// move to the up entry in the direction matrix
		if(row > 0)
			LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col);
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	char* pStr1="abcde";
	char* pStr2="acde";
	LCS(pStr1,pStr2);
	printf("\n");
	system("pause");
	return 0;
}</iostream>

代码三:

#include <iostream>
using namespace std;
 
/* 滚动数组 */
 
int dp[2][21];  /* 存储LCS长度 */
char X[21];
char Y[21];
int i, j, k;
 
void main()
{
    cin.getline(X,20);
    cin.getline(Y,20);
 
    int xlen = strlen(X);
    int ylen = strlen(Y);
 
    for(i = 1; i <= xlen; ++i)
    {
        k = i & 1;
        for(j = 1; j <= ylen; ++j)
        {
            if(X[i-1] == Y[j-1])
            {
                dp[k][j] = dp[k^1][j-1] + 1;
            }else if(dp[k][j-1] > dp[k^1][j])
            {
                dp[k][j] = dp[k][j-1];
            }else
            {
                dp[k][j] = dp[k^1][j];
            }
        }
    }
    printf("len of LCS is: %d\n", dp[k][ylen]);
}

//只能打印一个最长公共子序列
#include <iostream>
using namespace std;
 
 const int X = 100, Y = 100;        //串的最大长度
 char result[X+1];                    //用于保存结果
 int count=0;                        //用于保存公共最长公共子串的个数

 /*功能:计算最优值
  *参数:
  *        x:字符串x
  *        y:字符串y
  *        b:标志数组
  *        xlen:字符串x的长度
  *        ylen:字符串y的长度
  *返回值:最长公共子序列的长度
  *
  */
 int Lcs_Length(string x, string y, int b[][Y+1],int xlen,int ylen) 
 {
     int i = 0;
     int j = 0;

     int c[X+1][Y+1];
     for (i = 0; i<=xlen; i++) 
     {
         c[i][0]=0;
     } 
     for (i = 0; i <= ylen; i++ ) 
     {
         c[0][i]=0;
     }
     for (i = 1; i <= xlen; i++) 
     {
         
         for (j = 1; j <= ylen; j++) 
         {
             if (x[i - 1] == y[j - 1])
             {
                 c[i][j] = c[i-1][j-1]+1; 
                 b[i][j] = 1;
             }
             else 
                 if (c[i-1][j] > c[i][j-1]) 
                 {
                     c[i][j] = c[i-1][j]; 
                     b[i][j] = 2;
                 }
                 else 
                     if(c[i-1][j] <= c[i][j-1])
                     {
                         c[i][j] = c[i][j-1]; 
                         b[i][j] = 3;
                     }
                     
         }
     }
     cout << "计算最优值效果图如下所示:" << endl;
     for(i = 1; i <= xlen; i++)
     {
         for(j = 1; j < ylen; j++)
         {
             cout << c[i][j] << " ";
         }
         cout << endl;
     }
     return c[xlen][ylen];
 }
 
void Display_Lcs(int i, int j, string x, int b[][Y+1],int current_Len)
 {
     if (i ==0 || j==0) 
     {
         return;
     }
     if(b[i][j]== 1) 
     { 
         current_Len--;
         result[current_Len]=x[i- 1];
         Display_Lcs(i-1, j-1, x, b, current_Len);
     }
     else 
     {
         if(b[i][j] == 2)
         {
             Display_Lcs(i-1, j, x, b, current_Len);
         }
         else 
         {
             if(b[i][j]==3)
             {
                 Display_Lcs(i, j-1, x, b, current_Len);
             }
             else
             {
                 Display_Lcs(i-1,j,x,b, current_Len);
             }
         }
     }
 }
 
 int main(int argc, char* argv[])
 {
     string x = "ABCBDAB";
     string y = "BDCABA";
     int xlen = x.length();
     int ylen = y.length();

     int b[X + 1][Y + 1];

     int lcs_max_len = Lcs_Length( x, y, b, xlen,ylen );
     cout << lcs_max_len << endl;

     Display_Lcs( xlen, ylen, x, b, lcs_max_len );
     
     //打印结果如下所示
    for(int i = 0; i < lcs_max_len; i++)
    {
        cout << result[i];
    }
    cout << endl;
     return 0;
 }
代码四:
<pre name="code" class="cpp">//求取所有的最长公共子序列
#include <iostream>
using namespace std;
 
 const int X = 100, Y = 100;        //串的最大长度
 char result[X+1];                    //用于保存结果
 int count=0;                        //用于保存公共最长公共子串的个数

 /*功能:计算最优值
  *参数:
  *        x:字符串x
  *        y:字符串y
  *        b:标志数组
  *        xlen:字符串x的长度
  *        ylen:字符串y的长度
  *返回值:最长公共子序列的长度
  *
  */
 int Lcs_Length(string x, string y, int b[][Y+1],int xlen,int ylen) 
 {
     int i = 0;
     int j = 0;

     int c[X+1][Y+1];
     for (i = 0; i<=xlen; i++) 
     {
         c[i][0]=0;
     } 
     for (i = 0; i <= ylen; i++ ) 
     {
         c[0][i]=0;
     }
     for (i = 1; i <= xlen; i++) 
     {
         
         for (j = 1; j <= ylen; j++) 
         {
             if (x[i - 1] == y[j - 1])
             {
                 c[i][j] = c[i-1][j-1]+1; 
                 b[i][j] = 1;
             }
             else 
                 if (c[i-1][j] > c[i][j-1]) 
                 {
                     c[i][j] = c[i-1][j]; 
                     b[i][j] = 2;
                 }
                 else 
                     if(c[i-1][j] < c[i][j-1])
                     {
                         c[i][j] = c[i][j-1]; 
                         b[i][j] = 3;
                     }
                     else
                     {
                         c[i][j] = c[i][j-1]; //或者c[i][j]=c[i-1][j];
                         b[i][j] = 4;
                     }
         }
     }
     cout << "计算最优值效果图如下所示:" << endl;
     for(i = 1; i <= xlen; i++)
     {
         for(j = 1; j < ylen; j++)
         {
             cout << c[i][j] << " ";
         }
         cout << endl;
     }
     return c[xlen][ylen];
 }
 
 /*功能:计算最长公共子序列
  *参数:
  *        xlen:字符串x的长度
  *        ylen:字符串y的长度
  *        x    :字符串x
  *        b:标志数组
  *        current_len:当前长度
  *        lcs_max_len:最长公共子序列长度
  *
  */
 void Display_Lcs(int i, int j, string x, int b[][Y+1],int current_len,int lcs_max_len) 
 {
     if (i ==0 || j==0) 
     {
         for(int s=0; s < lcs_max_len; s++)
         {
             cout << result[s];
         }
         cout<<endl;
         count++;
         return;
     }
     if(b[i][j]== 1) 
     { 
         current_len--;
         result[current_len]=x[i- 1];
         Display_Lcs(i-1, j-1, x, b,current_len,lcs_max_len);
     }
     else 
     {
         if(b[i][j] == 2)
         {
             Display_Lcs(i-1, j, x, b,current_len,lcs_max_len);
         }
         else 
         {
             if(b[i][j]==3)
             {
                 Display_Lcs(i, j-1, x, b,current_len,lcs_max_len);
             }
             else
             {
                 Display_Lcs(i,j-1,x,b,current_len,lcs_max_len);
                 Display_Lcs(i-1,j,x,b,current_len,lcs_max_len);
             }
         }
     }
 }
 
 int main(int argc, char* argv[])
 {
     string x = "ABCBDAB";
     string y = "BDCABA";
     int xlen = x.length();
     int ylen = y.length();

     int b[X + 1][Y + 1];

     int lcs_max_len = Lcs_Length( x, y, b, xlen,ylen );
     cout << lcs_max_len << endl;

     Display_Lcs( xlen, ylen, x, b, lcs_max_len, lcs_max_len );
     cout << "共有:" << count << "种";

  
     return 0;
 }

抱歉!评论已关闭.