题目链接:http://poj.org/problem?id=1458
题目大意:给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到,而且每个字符的先后顺序和原串中的先后顺序一致。
输入有若干行,每行是两个字符串。对每一行输入的两个字符串,输出最长公共子串的长度。
Sample Input
abcfbc abfcab
programming contest
abcd mnp
Sample Output
4
2
0
算法分析
参考1:北大郭炜老师mooc课程
参考2:http://blog.csdn.net/u013480600/article/details/40741333
参考3:http://blog.csdn.net/lz161530245/article/details/76943991
输入两个串s1,s2,
设MaxLen(i,j)表示:s1的左边i个字符形成的子串,与s2左边的j个字符形成的子串的最长公共子序列的长度(i,j从0开始算)
MaxLen(i,j) 就是本题的“状态”
假定 len1 = strlen(s1),len2 = strlen(s2)
那么题目就是要求 MaxLen(len1,len2)
显然:
MaxLen(n,0) = 0 ( n= 0…len1)
MaxLen(0,n) = 0 ( n=0…len2)
递推公式:
if(s1[i-1] == s2[j-1]) //s1的最左边字符是s1[0]
MaxLen(i,j) = MaxLen(i-1,j-1) + 1;
else
MaxLen(i,j) = Max(MaxLen(i,j-1),MaxLen(i-1,j) );
时间复杂度O(mn),其中m,n是两个字串长度。
关于证明,可以阅读参考2和参考3的证明过程。大概过程记录如下:
我们用Ax表示序列A的连续前x项构成的子序列,即Ax= a1,a2,……ax, By= b1,b2,……by, 我们用LCS(x, y)表示它们的最长公共子序列长度,那原问题等价于求LCS(m,n)。为了方便我们用L(x, y)表示Ax和By的一个最长公共子序列。 让我们来看看如何求LCS(x, y)。我们令x表示子序列,考虑最后一项 第(1)种情况:Ax = By 那么它们L(Ax, By)的最后一项一定是这个元素! 为什么呢?为了方便,我们令t=Ax=By, 我们用反证法:假设L(x,y)最后一项不是t, 则要么L(x,y)为空序列(别忘了这个),要么L(x,y)的最后一项是Aa=Bb ≠ t, 且显然有a<x,b<y。无论是哪种情况我们都可以把t接到这个L(x,y)后面,从而得到一个更长的公共子序列。矛盾! 如果我们从序列Ax中删掉最后一项ax得到Ax-1,从序列By中也删掉最后一项by得到By-1,(多说一句角标为0时,认为子序列是空序列),则我们从L(x,y)也删掉最后一项t得到的序列是L(x – 1, y - 1)。为什么呢?和上面的道理相同,如果得到的序列不是L(x - 1, y - 1),则它一定比L(x - 1, y - 1)短,那么它后面接上元素t得到的子序列L(x,y)也比L(x - 1, y - 1)接上元素t得到的子序列短,这与L(x, y)是最长公共子序列矛盾。 因此L(x,y)=L(x-1,y-1)最后接上元素t,也就是说: LCS(Ax, By) = LCS(x - 1, y - 1) + 1 第(2)种情况:Ax ≠ By 仍然设t=L(Ax,By)的最后一个字符,或者L(Ax,By)是空序列(这时t是未定义值不等于任何值)。 则t≠Ax和t≠By至少有一个成立,因为t不能同时等于两个不同的值嘛! (2.1) 如果t≠Ax,则有L(x,y)=L(x-1,y),因为根本没Ax的事嘛。 也就是说:LCS(x,y) = LCS(x – 1, y) (2.2) 如果t≠By,同理有L(x,y)= L(x,y-1)。 也就是说:LCS(x,y) = LCS(x, y – 1) 可是,我们事先并不知道t,由定义,我们取最大的一个,因此这种情况下,有LCS(x,y)=max(LCS(x–1,y),LCS(x,y–1))。 看看目前我们已经得到了什么结论: LCS(x,y) = (1) LCS(x - 1,y - 1) + 1 如果Ax = By (2) max(LCS(x – 1, y) , LCS(x, y – 1)) 如果Ax ≠ By 这是一个显然的递推式,光有递推可不行,初值是什么呢? 显然,一个空序列和任何序列的最长公共子序列都是空序列!所以我们有: LCS(x,y) = (1) LCS(x - 1,y - 1) + 1 如果Ax = By (2) max(LCS(x – 1, y) , LCS(x, y – 1)) 如果Ax ≠ By (3) 0 如果x=0或者y=0 到此我们求出了计算最长公共子序列长度的递推公式。我们实际上计算了一个(n + 1)行(m + 1)列的表格(行是0..n,列是0..m),也就这个二维度数组LCS(n,m)。