问题描述
子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串算法
好比序列bo, bg, lg在母串cnblogs与belong中都出现过而且出现顺序与母串保持一致,咱们将其称为公共子序列。最长公共子序列(Longest Common Subsequence,LCS),顾名思义,是指在全部的子序列中最长的那一个。子串是要求更严格的一种子序列,要求在母串中连续地出现。在上述例子的中,最长公共子序列为blog(cnblogs,belong),最长公共子串为lo(cnblogs, belong)。
求解算法
对于母串X=<x1,x2,⋯,xm>, Y=<y1,y2,⋯,yn>,求LCS与最长公共子串。
假设Z=<z1,z2,⋯,zk>是X与Y的LCS, 咱们观察到:
- 若是Xm=Yn,则Zk=Xm=Yn,有:Zk−1是Xm−1与Yn−1的LCS;
- 若是Xm≠Yn,则Zk是Xm与Yn−1的LCS,或者是Xm−1与Yn的LCS。数组
所以,求解LCS的问题则变成递归求解的两个子问题。可是,上述的递归求解的办法中,重复的子问题多,效率低下。改进的办法——用空间换时间,用数组保存中间状态,方便后面的计算。这就是动态规划(DP)的核心思想了。
DP求解LCS
用二维数组c[i][j]记录串x1,x2,⋯,xi与y1,y2,⋯,yj的LCS长度,则可获得状态转移方程:
由最长公共子序列问题的最优子结构性质可知,要找出X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列,可按如下方式递归地进行:当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,而后在其尾部加上xm(=yn)便可得X和Y的一个最长公共子序列。当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。
在算法LCS中,每一次的递归调用使i或j减1,所以算法的计算时间为O(m+n)。
代码实现spa
1 int lcs(String str1, String str2) { 2 int len1 = str1.length(); 3 int len2 = str2.length(); 4 int c[][] = new int[len1 + 1][len2 + 1]; 5 for (int i = 0; i <= len1; i++) { 6 for (int j = 0; j <= len2; j++) { 7 if (i == 0 || j == 0) { 8 c[i][j] = 0; 9 } else if (str1.charAt(i - 1) == str2.charAt(j - 1)) { 10 c[i][j] = c[i - 1][j - 1] + 1; 11 } else { 12 c[i][j] = max(c[i - 1][j], c[i][j - 1]); 13 } 14 } 15 } 16 return c[len1][len2]; 17 }