前言:学习过的知识,只要不经常使用就会忘记,所以在此写博客,记录下来,方便自己,也可能有利于他人。
最长公共子序列(LCS)问题。
1.什么是最长公共子序列?
最长公共子序列,英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
注:最长公共子序列和最长公共子串的区别:前者(公共子序列)不必连续,后者(字串)必须是连续的
如:abccfg与adcf:最长公共子序列为:acf,而最长公共字串为:cf。(一目了然了吧)。
2.解决方法
1).求LCS肯能最先想到的就是穷举法,如X和Y两个序列,通过穷举X的每个子序列,看是否在Y中,并选择最长的序列即为最后结果,但是此方法的时间复杂度较大,一般不用此方法。
如X的一个子序列相对应下标为{1,2,3.....n},那么X共有2^n个子序列(注意包含空序列),Y也同理为2^m个,所以其时间复杂度为O(2^n*2^m)。
2).动态规划法求解
关于此方法的详细解答http://blog.csdn.net/v_JULY_v/article/details/6695482已经给出了超详细的描述。此处根据下述思想写出代码。(原文中没有打印出所有的最长公共子序列,此处稍作修改,打印出所有子序列)
最长公共子序列的结构有如下表示:
设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:
- 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
- 若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;
- 若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
- #include <iostream>
- using namespace std;
- #define x 100 //串的最大长度
- #define y 100
- char result[x]; //保存序列的结果
- int count=0; //记录LCS的个数
- /*功能:返回最长序列的长度
- x:字串串x
- y:字符串y
- b:标志数组
- xlen:字符串x的长度
- ylen:字符串y的长度
- */
- int Lcs_length(string x_s,string y_s,int b[][y+1],int xlen,int ylen)
- {
- int i=0,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_s[i-1]==y_s[j-1])
- {
- c[i][j]=c[i-1][j-1]+1;
- b[i][j]=1; //在b数组中进行标志,便于后面打印。
- }
- 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];
- b[i][j]=4;
- }
- }
- }
- return c[xlen][ylen];
- }
- /*功能:打印出LCS
- i:字符串x的长度
- j:字符串y的长度
- x:字符串x
- b:标志数组
- current_len:当前子序列长度
- lcs_max_len:最长公共子序列长度
- */
- void Display_LCS(int i,int j,string x_s,int b[][y+1],int current_len,int lcs_max_len)
- {
- if(i==0||j==0)
- {
- for(int k=0;k<lcs_max_len;k++)
- {
- cout<<result[k];
- }
- cout<<endl;
- count++;
- return;
- }
- if(b[i][j]==1)
- {
- current_len--; //Xm=Yn的情况,就将子序列长度减1,并保存一个元素。
- result[current_len]=x_s[i-1];
- Display_LCS(i-1,j-1,x_s,b,current_len,lcs_max_len);
- }
- else if(b[i][j]==2) //Xm!=Zm的情况,(具体参考上述链接博文有详细说明)
- Display_LCS(i-1,j,x_s,b,current_len,lcs_max_len);
- else if(b[i][j]==3) //Yn!=Zm的情况
- Display_LCS(i,j-1,x_s,b,current_len,lcs_max_len);
- else //相等的情况c[i-1][j]==c[i][j-1]
- {
- Display_LCS(i-1,j,x_s,b,current_len,lcs_max_len);
- Display_LCS(i,j-1,x_s,b,current_len,lcs_max_len);
- }
- }
- int main()
- {
- string test1 = "ABCBDAB";
- string test2 = "BDCABA";
- int xlen = test1.length();
- int ylen = test2.length();
- int b[x+ 1][y+1];
- int lcs_max_len = Lcs_length( test1,test2,b,xlen,ylen);
- Display_LCS( xlen, ylen, test1, b, lcs_max_len, lcs_max_len );
- cout << "最长公共子序列共有: "<< count <<"种"<<endl;
- return 0;
- }
再次推荐此博客http://blog.csdn.net/v_JULY_v/article/details/6695482。
cdx 2014.4.25 20:25