最長公共子序列問題又稱LCS問題(longest common subsequence problem)
問題描述:
給你兩個字符串str1和str2,它們之間可能存在公有子序列,子序列和子串的區別是:子序列不要求連續,只需要按照順序出現就好,子串則要求連續:
例如:SIMPLE和NAIVE有共同的子序列IE,但是沒有共同的子串。
TOO SIMPLE和TOO YOUNG則有共同子串TOO
LCS問題就是(1)求出最長公有子序列的長度(2)求出最長公有子序列。
(1) 求出最長公有子序列的長度
解法考慮動態規划,用一個二維數組L [m][n]存狀態, L [i][j]的含義是str1前i項和str2的前j項最長的公共子序列的長度。
寫出狀態轉移方程:
第二個式子比較好理解,
第三個我是這么理解的,本應該是求:
可是很顯然第三項是最小的,所以省略了。
(2)輸出一個LCS
從狀態表右下角開始往前回溯,當前字符相等,則存入結果字符串,當前字符不相等,比較狀態表上方與左方的值,並移動到最大值位置,重復上述步驟,直到到達邊界。反序輸出結果字符串。
上代碼:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 int LCS(char*a,char*b){ 5 int m=strlen(a); 6 int n=strlen(b); 7 int L[m][n]; 8 int i,j; 9 char res[20]; 10 int k=0; 11 12 for(i=0;i<m;i++){ 13 for(j=0;j<n;j++){ 14 if(i==0||j==0){ 15 L[i][j]=0; //case 1 16 }else{ 17 if(a[i]==b[j]){ 18 L[i][j]=L[i-1][j-1]+1; //case 2 19 }else{ 20 L[i][j]=L[i-1][j]>L[i][j-1]?L[i-1][j]:L[i][j-1]; // case 3 21 } 22 } 23 printf("%d ",L[i][j]); 24 } 25 printf("\n"); 26 } 27 28 for(i=m-1,j=n-1;i>=0&&j>=0;){ 29 if(a[i]==b[j]){ 30 res[k++]=a[i]; 31 i--; 32 j--; 33 34 }else{ 35 if(L[i-1][j]>L[i][j-1]){ 36 i--; 37 }else{ 38 j--; 39 } 40 } 41 } 42 43 printf("\nLCS:"); 44 for(i=strlen(res)-1;i>=0;i--){ 45 printf("%c",res[i]); 46 } 47 printf("\n"); 48 49 return L[m-1][n-1]; 50 } 51 int main(){ 52 char str1[50]; 53 char str2[50]; 54 strcpy(str1,"SIMPLE"); 55 strcpy(str2,"NAIVE"); 56 int ret; 57 58 ret=LCS(str1,str2); 59 printf("length of LCS:%d\n",ret); 60 return 0; 61 }