最长公共子序列PK最长公共子串
1、先科普下最长公共子序列 & 最长公共子串的区别:
找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。而最长公共子序列则并不要求连续。
(1)递归方法求最长公共子序列的长度
1)设有字符串a[0...n],b[0...m],下面就是递推公式。
当数组a和b对应位置字符相同时,则直接求解下一个位置;当不同时取两种情况中的较大数值。
2)代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 char a[30],b[30]; 4 int lena,lenb; 5 int LCS(int,int); ///两个参数分别表示数组a的下标和数组b的下标 6 7 int main() 8 { 9 strcpy(a,"ABCBDAB"); 10 strcpy(b,"BDCABA"); 11 lena=strlen(a); 12 lenb=strlen(b); 13 printf("%d\n",LCS(0,0)); 14 return 0; 15 } 16 17 int LCS(int i,int j) 18 { 19 if(i>=lena || j>=lenb) 20 return 0; 21 if(a[i]==b[j]) 22 return 1+LCS(i+1,j+1); 23 else 24 return LCS(i+1,j)>LCS(i,j+1)? LCS(i+1,j):LCS(i,j+1); 25 }
用递归的方法优点是编程简单,容易理解。缺点是效率不高,有大量的重复执行递归调用,而且只能求出最大公共子序列的长度,求不出具体的最大公共子序列。
(2)动态规划求最长公共子序列的长度
动态规划采用二维数组来标识中间计算结果,避免重复的计算来提高效率。
1)最长公共子序列的长度的动态规划方程
设有字符串a[0...n],b[0...m],下面就是递推公式。字符串a对应的是二维数组num的行,字符串b对应的是二维数组num的列。
代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 4 char a[500],b[500]; 5 char num[501][501]; ///记录中间结果的数组 6 char flag[501][501]; ///标记数组,用于标识下标的走向,构造出公共子序列 7 void LCS(); ///动态规划求解 8 void getLCS(); ///采用倒推方式求最长公共子序列 9 10 int main() 11 { 12 int i; 13 strcpy(a,"ABCBDAB"); 14 strcpy(b,"BDCABA"); 15 memset(num,0,sizeof(num)); 16 memset(flag,0,sizeof(flag)); 17 LCS(); 18 printf("%d\n",num[strlen(a)][strlen(b)]); 19 getLCS(); 20 return 0; 21 } 22 23 void LCS() 24 { 25 int i,j; 26 for(i=1;i<=strlen(a);i++) 27 { 28 for(j=1;j<=strlen(b);j++) 29 { 30 if(a[i-1]==b[j-1]) ///注意这里的下标是i-1与j-1 31 { 32 num[i][j]=num[i-1][j-1]+1; 33 flag[i][j]=1; ///斜向下标记 34 } 35 else if(num[i][j-1]>num[i-1][j]) 36 { 37 num[i][j]=num[i][j-1]; 38 flag[i][j]=2; ///向右标记 39 } 40 else 41 { 42 num[i][j]=num[i-1][j]; 43 flag[i][j]=3; ///向下标记 44 } 45 } 46 } 47 } 48 49 void getLCS() 50 { 51 52 char res[500]; 53 int i=strlen(a); 54 int j=strlen(b); 55 int k=0; ///用于保存结果的数组标志位 56 while(i>0 && j>0) 57 { 58 if(flag[i][j]==1) ///如果是斜向下标记 59 { 60 res[k]=a[i-1]; 61 k++; 62 i--; 63 j--; 64 } 65 else if(flag[i][j]==2) ///如果是斜向右标记 66 j--; 67 else if(flag[i][j]==3) ///如果是斜向下标记 68 i--; 69 } 70 71 for(i=k-1;i>=0;i--) 72 printf("%c",res[i]); 73 }
(3)图示
最长公共子串
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 Main mainObj = new Main(); 7 int len = mainObj.getCommonStrLength(sc.next(),sc.next()); 8 System.out.println(len); 9 } 10 11 int getCommonStrLength(String str1, String str2) { 12 str1 = str1.toLowerCase(); 13 str2 = str2.toLowerCase(); 14 int len1 = str1.length(); 15 int len2 = str2.length(); 16 String min = null; 17 String max = null; 18 String target = null; 19 min = len1 <= len2 ? str1 : str2; 20 max = len1 > len2 ? str1 : str2; 21 //最外层:min子串的长度,从最大长度开始 22 for (int i = min.length(); i >= 1; i--) { 23 //遍历长度为i的min子串,从0开始 24 for (int j = 0; j <= min.length() - i; j++) { 25 target = min.substring(j, j + i); 26 //遍历长度为i的max子串,判断是否与target子串相同,从0开始 27 for (int k = 0; k <= max.length() - i; k++) { 28 if (max.substring(k,k + i).equals(target)) { 29 return i; 30 } 31 } 32 } 33 } 34 return 0; 35 } 36 }
Jumping from failure to failure with undiminished enthusiasm is the big secret to success.