一、什么是最长公共子序列
什么是最长公共子序列呢?举个简单的例子吧,一个数列S,若分别是两个或多个已知序列的子序列,且是所有符合条件序列中最长的,则S称为已知序列的最长公共子序列。
举例如下,如:有两个随机数列,1 2 3 4 5 6 和 3 4 5 8 9,则它们的最长公共子序列便是:3 4 5。
一直不明白:最长公共子串和最长公共子序列的区别。
上网查了下,最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。
二、蛮力法
蛮力法是解决最长公共子序列问题最容易想到的方法,即对S的每一个子序列,检查是否为T的子序列,从而确定它是否为S和T的公共子序列,并且选出最长的公共子序列。
S和T的所有子序列都检查过后即可求出S和T的最长公共子序列。S的一个子序列相应于下标序列1,2,...,n的一个子序列。因此,S共有2^n个子序列。当然,T也有2^m个子序列。
因此,蛮力法的时间复杂度为O(2^n * 2^m),这可是指数级别的啊。
三、动态规划方法
1、序列str1和序列str2
·长度分别为m和n;
·创建1个二维数组L[m.n];
·初始化L数组内容为0
·m和n分别从0开始,m++,n++循环:
- 如果str1[m] == str2[n],则L[m,n] = L[m - 1, n -1] + 1;
- 如果str1[m] != str2[n],则L[m,n] = max{L[m,n - 1],L[m - 1, n]}
·最后从L[m,n]中的数字一定是最大的,且这个数字就是最长公共子序列的长度
·从数组L中找出一个最长的公共子序列
2、从数组L中查找一个最长的公共子序列
i和j分别从m,n开始,递减循环直到i = 0,j = 0。其中,m和n分别为两个串的长度。
·如果str1[i] == str2[j],则将str[i]字符插入到子序列内,i--,j--;
·如果str1[i] != str[j],则比较L[i,j-1]与L[i-1,j],L[i,j-1]大,则j--,否则i--;(如果相等,则任选一个)
图1 效果演示图
根据上图,我们可以得到其中公共子串:B C B A 和 B D A B。
总感觉,上面这个过程说的不是很清楚,但是不知道怎么才能更加清楚的表述??纠结啊。
四、代码实现
- //代码实现比较简单,有可能不符合规矩,如有哪位前辈看到后,可以指出,我会虚心学习。
- 1 #include
- <iostream>
- 2 #include
- <string>
- 3 using namespace std
- ;
- 4
- int main(int argc, char **argv)
- 5
- {
- 6
- string str1 = "ABCBDAB";
- 7
- string str2 = "BDCABA";
- 8
- 9
- int x_len = str1.length();
- 10
- int y_len = str2.length();
- 11
- 12
- int arr[50][50] = {{0,0}};
- 13
- 14
- int i = 0;
- 15
- int j = 0;
- 16
- 17
- for(i = 1; i <= x_len; i++)
- 18
- {
- 19
- for(j = 1; j <= y_len; j++)
- 20
- {
- 21
- if(str1[i - 1] == str2[j - 1])
- 22
- {
- 23
- arr[i][j] = arr[i - 1][j - 1] + 1;
- 24
- }
- 25
- else
- 26
- {
- 27
- 28
- if(arr[i][j - 1] >= arr[i - 1][j])
- 29
- {
- 30
- arr[i][j] = arr[i][j - 1];
- 31
- }
- 32
- else
- 33
- {
- 34
- arr[i][j] = arr[i -1][j];
- 35
- }
- 36
- }
- 37
- 38
- }
- 39
运行结果如下所示。
- }
- 41 for(i = 0 ; i <= x_len; i++)
- 42 {
- 43 for( j = 0; j <= y_len; j++)
- 44 {
- 45 cout << arr[i][j] << " ";
- 46 }
- 47 cout << endl;
- 48 }
- 49 for(i = x_len, j = y_len; i >= 1 && j >= 1;)
- 50 {
- 51 if(str1[i - 1] == str2[j - 1])
- 52 {
- 53 cout << str1[i - 1] << " ";//倒序打印的
- 54 i--;
- 55 j--;
- 56 }
- 57 else
- 58 {
- 59 // if(arr[i][j -1] >= arr[i - 1][j])//打印:B A D B
- 60 if(arr[i][j -1] > arr[i - 1][j]) //打印:A B C B
- 61 {
- 62 j--;
- 63 }
- 64 else
- 65 {
- 66 i--;
- 67 }
- 68 }
- 69 }
- 70 cout << endl;
- 71 return 0;
- 72 }
图2 运行效果最后输出为A B C B,则最大子串为B C B A。其实,应该将结果保存起来,然后,正序打印呢。备注:我想还应该有更加优化的方法:求取最大公共子序列。关门了,明天看看。如果哪位前辈知道哪里有更加详细的介绍动态规划求最大公共子序列的资料,可以告诉我下,不胜感激。如果看到这些文字,对于文字中,有哪些不合适合理的地方,请定要指出,也不胜感激,这样我才可以更好的进步。
- }
- }
- }
- arr[i][j] = arr[i -1][j];
- {
- else
- }
- arr[i][j] = arr[i][j - 1];
- {
- if(arr[i][j - 1] >= arr[i - 1][j])
- {
- else
- }
- arr[i][j] = arr[i - 1][j - 1] + 1;
- {
- if(str1[i - 1] == str2[j - 1])
- {
- for(j = 1; j <= y_len; j++)
- {
- for(i = 1; i <= x_len; i++)
- int j = 0;
- int i = 0;
- int arr[50][50] = {{0,0}};
- int y_len = str2.length();
- int x_len = str1.length();
- string str2 = "BDCABA";
- string str1 = "ABCBDAB";
- {
- int main(int argc, char **argv)
- ;
- <string>
- <iostream>