最长公共子序列问题
- 引言
目的是高效的解决问题,不是某个算法的粉丝。
- 题目
找出两个字符串的最长的公共子序列。
- 思路
首先我们要学会分析题目,然后去找题目的规律,从而想出解法。拿个例子来说吧,容易讨论;string SA = "BHEAJEIICE";string SB = "FFBHBBFCHG";
然后我们就分析,如果我们用穷举法怎么去怎么解决呢?我们会求出这两个字符串的所有字串(元素可能是不连着的),接着找出所有的公共字串,然后再这些当中找出那个最长的字串即可。然后我们就分析一下我们的暴力法,先不说时间复杂度,第一步求字串就很复杂(根据字串的特点,长度由1-n,可以利用DP求出,时间复杂度已经高的不行不行了,几乎接近O(n^n)),所以暴力法只能放弃了。接着我就想,题目给的这两个字符串的长度都很长,不便于直观去想怎么办,那我就给他缩小规模,直至我可以去直观地思考解决它,然后再找出规律。(为什么可以这么做呢,稍作分析,这个问题包含子结构,渐渐的我们靠向了DP,然后我们是否符合DP的使用条件呢,在扩大字符串规模的时候发现包含重叠子问题;那么从哪边扩大呢,方向都是可以的,并且剩下规模字符串考虑不受前面字符串的影响,也就是说符合第二个条件无后向性;那么用最优子结构的性质去确定状态变换方程,这个题目也就迎刃而解了)
在扩大规模的时候,考虑到SA[m]与SB[n]进行匹配的时候如下;
if SA[m] != SB[n]so the Longest common sub-list must be in(SA[0 ~~ m-1] , SB[0 ~~ n]) or (SA[0 ~~ m] , SA[0 ~~ n-1]) ;elsethe longest common sub-list must be (SA[0 ~~ m-1] , SB[0 ~~ n-1]) +1 ;
- 实验
利用以上实验数据和随机数据做实验,实验结果如下first one:
second one:
- 代码
test.cpp(代码很水,很久以前写的,只供学习算法)#include <iostream> #include <fstream> #include <list> using namespace std; class Longest_commen_sublist { char *X ; char *Y ; char **b ; int **c ; public: Longest_commen_sublist( ) ; void LCS_LENGTH( int m ,int n ) ; void XY_set( int x,int y ); void PRINT_LCS(int i,int j); void Main(); ~Longest_commen_sublist(void) ; }; Longest_commen_sublist::Longest_commen_sublist( ) { //X = new list<char>; //Y = new list<char>; } void Longest_commen_sublist::XY_set( int x ,int y ) { X = new char[x+1]; Y = new char[y+1]; ofstream fileWriter; fileWriter.open("XY.txt"); fileWriter.clear(); int i = 1 ; while( i<=x ) { X[i]='A'+rand()%10; fileWriter<<X[i]<<'/'; cout<<X[i]; i++; } fileWriter<<'\n'; cout<<endl; i = 1 ; while( i<=y ) { Y[i]='A'+rand()%10; fileWriter<<Y[i]<<'/'; cout<<Y[i]; i++; } cout<<endl; fileWriter.close(); } void Longest_commen_sublist::LCS_LENGTH( int m ,int n ) { int i,j ; c = new int*[m+1]; // apply space for table b and c; b = new char*[m+1]; for( i =0 ;i<=m;i++ ) { c[i] = new int[n+1]; b[i] = new char[n+1]; } for(i= 1;i<=m;i++) c[i][0] = 0 ; for(j= 0;j<=n;j++) c[0][j] = 0 ; for( i =1 ;i<=m;i++ ) for( j=1 ;j<=n;j++ ) { if(X[i] == Y[j]) { c[i][j] = c[i-1][j-1]+1; b[i][j] ='\\'; } else if(c[i-1][j]>=c[i][j-1]) { c[i][j]=c[i-1][j]; b[i][j] ='|'; }else { c[i][j]=c[i][j-1]; b[i][j] = '-'; } } } void Longest_commen_sublist::PRINT_LCS( int i,int j) { if( i !=0 && j!=0 ) { if( b[i][j] == '\\' ) { this->PRINT_LCS(i-1,j-1) ;//why the sequeue using the stack cout<<i<<"\t"<<X[i]<<endl ; } else if(b[i][j]=='|') this->PRINT_LCS(i-1,j); else this->PRINT_LCS(i,j-1); } } void Longest_commen_sublist::Main( ) { int i = 50 ; this -> XY_set( i ,i ) ; this->LCS_LENGTH(i,i); this->PRINT_LCS(i,i); } Longest_commen_sublist::~Longest_commen_sublist(void) { delete X; delete Y; } int main() { Longest_commen_sublist * LCS = new Longest_commen_sublist() ; LCS -> Main() ; delete LCS ; system("pause") ; return 0 ; }