最长公共子序列

想要了解最长公共子序列首先要清楚这些概念:

<1> 子序列:就是在原序列中找出一部分组成的序列。 例:字符串“abcdefg” 的子序列有 “ac”、“ade”....一共有27个子序列

<2> 最长公共子序列:一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。

           例:字符串“cnblogs”和“belong”的最长公共子序列为“blog”。

           仔细想想我们可以发现其实最长公共子序列的个数不是唯一的,可能会有两个以上,

           但是长度一定是唯一的,比如这里的最长公共子序列的长度为4。

<3> 子串:串中任意个连续的字符组成的子序列称为该串的子串

<4> 最长公共子串:两个字符串中,最长的相同子字符串的长度

 

最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:

      子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。

 

最长公共子序列的应用:最长公共子序列是一个十分实用的问题,它可以描述两段文字之间的“相似度”,

           即它们的雷同程度,从而能够用来辨别抄袭。对一段文字进行修改之后,

           计算改动前后文字的最长公共子序列,将除此子序列外的部分提取出来,

           这种方法判断修改的部分,往往十分准确。简而言之,百度知道、百度百科都用得上。

解决最长公共子序列问题 最好的选择 ---> “动态规划”

动态规划的计算两个序列的最长公共子序列的方法如下:

以两个序列 s1、s2 为例子:

设有二维数组x[i,j] 表示 s1 的 i 位和 s2 的 j 位之前的最长公共子序列的长度,则有:

x[1][1] = same(1,1);

x[i,j] = max{x[i-1][j -1] + same(i,j),x[i-1,j],x[i,j-1]}

其中,same(a,b)当 X 的第 a 位与 Y 的第 b 位相同时为“1”,否则为“0”。

此时,二维数组中最大的数便是 s1 和 s2 的最长公共子序列的长度,依据该数组回溯,便可找出最长公共子序列。

该算法的空间、时间复杂度均为O(n^2),经过优化后,空间复杂度可为O(n)。

 

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 char s1[1000],s2[1000];
 5 int x[1000][1000];   // 记录最长公共子序列 
 6 int y[1000][1000];   // 记录路径 
 7 int LCS()    
 8 {
 9     int i,j;
10     int l1 = strlen(s1);     // 计算字符串的长度 
11     int l2 = strlen(s2);
12     memset(x,0,sizeof(x));  // 初始化 过滤掉0的情况 
13     memset(y,0,sizeof(y));
14     
15     for (i = 1; i <= l1; i ++)
16     {
17         for (j = 1; j <= l2; j ++)
18         {
19             if (s1[i-1] == s2[j-1])   // 相等的情况 
20             {   // 字符数组是从0开始的 所以这里要减 1 
21                 x[i][j] = x[i-1][j-1]+1;
22                 y[i][j] = 1;          // 记录路径 
23             }
24             else if(x[i-1][j] >= x[i][j-1])    // 不相等的时候选择 比较“左边”和“上边”选择较大的 
25             {
26                 x[i][j] = x[i-1][j];
27                 y[i][j] = 2;          // 记录路径 
28             }
29             else
30             {
31                 x[i][j] = x[i][j-1];
32                 y[i][j] = 3;         // 记录路径 
33             }
34         }
35     }
36     return x[l1][l2];
37 }
38 void print_LCS(int i,int j)   // 根据标记路径的数组 输出最长公共子序列 
39 {
40     if (i == 0 || j == 0)
41         return ;
42     if (y[i][j] == 1)      
43     {
44         print_LCS(i-1,j-1);
45         printf("%c",s1[i-1]);
46     }
47     else if (y[i][j] == 2)  // 左边 
48         print_LCS(i-1,j);
49     else                    // 上边 
50         print_LCS(i,j-1);
51 }

 

posted @ 2017-04-09 22:46  gaoyanliang  阅读(817)  评论(0编辑  收藏  举报