最大公共子串 LCS
这个LCS跟前面说的最长公共子序列的LCS不一样,不过也算是LCS的一个变体,在LCS中,子序列是不必要求连续的,而子串则是“连续”的。即:
题:给定两个字符串X,Y,求二者最长的公共子串,例如X=[aaaba],Y=[abaa]。二者的最长公共子串为[aba],长度为3。
基本算法
其实对于最长公共子串,还是比较简单易想的,因为子串是连续的,这就方便了很多。最直接的方法就是用X每个子串与Y的每个子串做对比,求出最长的公共子串。代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
/* 最长公共子串 Longest Common Substring */ int maxlen; /* 记录最大公共子串长度 */ int maxindex; /* 记录最大公共子串在串1的起始位置 */ void outputLCS(char * X); /* 输出LCS */ /* 最长公共子串 基本算法 */ int comlen(char * p, char * q) { int len = 0; while(*p && *q && *p++ == *q++) { ++len; } return len; } void LCS_base(char * X, int xlen, char * Y, int ylen) { for(int i = 0; i < xlen; ++i) { for(int j = 0; j < ylen; ++j) { int len = comlen(&X[i],&Y[j]); if(len > maxlen) { maxlen = len; maxindex = i; } } } outputLCS(X); } |
==================================复杂度O(n^3)
动态规划
使用dp[i][j]表示 以x[i]和y[j]结尾的最长公共子串的长度,因为要求子串连续,所以对于X[i]与Y[j]来讲,它们要么与之前的公共子串构成新的公共子串;要么就是不构成公共子串。故状态转移方程 X[i] == Y[j],dp[i][j] = dp[i-1][j-1] + 1 X[i] != Y[j],dp[i][j] = 0 对于初始化,i==0或者j==0,如果X[i] == Y[j],dp[i][j] = 1;否则dp[i][j] = 0。
- /* 最长公共子串 DP */
- int dp[30][30];
- void LCS_dp(char * X, int xlen, char * Y, int ylen)
- {
- maxlen = maxindex = 0;
- for(int i = 0; i < xlen; ++i)
- {
- for(int j = 0; j < ylen; ++j)
- {
- if(X[i] == Y[j])
- {
- if(i && j)
- {
- dp[i][j] = dp[i-1][j-1] + 1;
- }
- if(i == 0 || j == 0)
- {
- dp[i][j] = 1;
- }
- if(dp[i][j] > maxlen)
- {
- maxlen = dp[i][j];
- maxindex = i + 1 - maxlen;
- }
- }
- }
- }
- outputLCS(X);
- }
复杂度分析:O(n^2);和最大公共子序列复杂度一样。
各方案复杂度对比
设字符串X的长度为m,Y的长度为n,最长公共子串长度为l。
对于基本算法,X的子串(m个)和Y的子串(n个)一一对比,最坏情况下,复杂度为O(m*n*l),空间复杂度为O(1)。
对于DP算法,由于自底向上构建最优子问题的解,时间复杂度为O(m*n);空间复杂度为O(m*n),当然这里是可以使用滚动数组来优化空间的,滚动数组在动态规划基础回顾中多次提到。


浙公网安备 33010602011771号