引入:
最长公共子序列常用于解决字符串的相似度问题。
最长公共子序列(Longest Common Subsequence,LCS)与最长公共字串(Longest Common Substring):子串是串的一个连续的部分,子序列则是从不改变序列顺序,而从序列中去掉任意多个元素而获得的新的序列;也就是说,子串中字符的位置一定是连续的,而子序列不一定连续。
a not the 之一(得到的未必就是唯一的那个最长公共子串,只有长度是唯一的)
——其余字符串问题,待续
解决方案:
1、穷举法(Bruke force alg )
a[m] and b[n]
check every sequence of a ,to see if it is a sequence of b .
analysis:
check = O(N)
2^m subsequence of a
so O(n*2^m)=slow
不可取:一个长度为N的字符串,其子序列有2^N个,指数级的复杂度。
2、递归法
对于字符串a[ ]和 b[ ],当a与b位置上的字符相同时,则直接求解下一个位置,当不同时取两种情况下较大的数值。
1 #include<stdio.h>
2
3 #include<string.h>
4
5 char a[30],b[30];
6
7 int lena,lenb;
8
9 int LCS(int,int); ///两个参数分别表示数组a的下标和数组b的下标
10
11 int main()
12
13 {
14
15 strcpy(a,"ABCBDAB");
16
17 strcpy(b,"BDCABA");
18
19 lena=strlen(a);
20
21 lenb=strlen(b);
22
23 printf("%d\n",LCS(0,0));
24
25 return 0;
26
27 }
28
29 int LCS(int i,int j)
30
31 {
32
33 if(i>=lena || j>=lenb)
34
35 return 0;
36
37 if(a[i]==b[j])
38
39 return 1+LCS(i+1,j+1);
40
41 else
42
43 return LCS(i+1,j)>LCS(i,j+1)? LCS(i+1,j):LCS(i,j+1);
44
45 }
代码实现
优点:容易理解,编程简单,
缺点:效率低下,有大量重复执行的递归调用,且只能求出最大公共子序列的长度,不能得到具体的公共子序列。
3、动态规划
改进:采用二维数组来标记中间结果,避免重复的计算以便提高效率。
对于字符串a[]和 b[],num[i][j]记录序列a[ i]和 b[j ]的最长公共子序列的长度,其中,a[ i]即{a1,a2,....,ai}和 b[j ]即{b1,b2,....,bj}
b[i][j]来记录c[i][j]的值是由哪一个子问题的解得到的。
1 #include<stdio.h>
2
3 #include<string.h>
4
5
6
7 char a[500],b[500];
8
9 char num[501][501]; ///记录中间结果的数组
10
11 char flag[501][501]; ///标记数组,用于标识下标的走向,构造出公共子序列
12
13 void LCS(); ///动态规划求解
14
15 void getLCS(); ///采用倒推方式求最长公共子序列
16
17
18
19 int main()
20
21 {
22
23 int i;
24
25 strcpy(a,"ABCBDAB");
26
27 strcpy(b,"BDCABA");
28
29 memset(num,0,sizeof(num));
30
31 memset(flag,0,sizeof(flag));
32
33 LCS();
34
35 printf("%d\n",num[strlen(a)][strlen(b)]);
36
37 getLCS();
38
39 return 0;
40
41 }
42
43
44
45 void LCS()
46
47 {
48
49 int i,j;
50
51 for(i=1;i<=strlen(a);i++)
52
53 {
54
55 for(j=1;j<=strlen(b);j++)
56
57 {
58
59 if(a[i-1]==b[j-1]) ///注意这里的下标是i-1与j-1
60
61 {
62
63 num[i][j]=num[i-1][j-1]+1;
64
65 flag[i][j]=1; ///斜向下标记
66
67 }
68
69 else if(num[i][j-1]>num[i-1][j])
70
71 {
72
73 num[i][j]=num[i][j-1];
74
75 flag[i][j]=2; ///向右标记
76
77 }
78
79 else
80
81 {
82
83 num[i][j]=num[i-1][j];
84
85 flag[i][j]=3; ///向下标记
86
87 }
88
89 }
90
91 }
92
93 }
94
95
96
97 void getLCS()
98
99 {
100
101
102
103 char res[500];
104
105 int i=strlen(a);
106
107 int j=strlen(b);
108
109 int k=0; ///用于保存结果的数组标志位
110
111 while(i>0 && j>0)
112
113 {
114
115 if(flag[i][j]==1) ///如果是斜向下标记
116
117 {
118
119 res[k]=a[i-1];
120
121 k++;
122
123 i--;
124
125 j--;
126
127 }
128
129 else if(flag[i][j]==2) ///如果是斜向右标记
130
131 j--;
132
133 else if(flag[i][j]==3) ///如果是斜向下标记
134
135 i--;
136
137 }
138
139
140
141 for(i=k-1;i>=0;i--)
142
143 printf("%c",res[i]);
144
145 }
146
147
代码实现
动态规划——最长公共子串,码迷,mamicode.com