http://blog.csdn.net/zztfj/article/details/6157429
LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。
比如:
String str1 = new String("adbccadebbca");
String str2 = new String("edabccadece");
str1与str2的公共子串就是bccade.
解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.
下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232
0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。下面是新的矩阵生成方式:
0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
0 1 0 0 0 0 0 0 0 2 1 0 0 0 0
1 0 2 0 1 0 1 0 0 0 0 0 1 0 0
0 2 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 3 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 4 0 0 0 2 1 0 0 1 0 0 0
1 0 1 0 5 0 1 0 0 0 0 0 2 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 2 0 0 0 2 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。具体算法如下:
public static void getLCString(char[] str1, char[] str2) { int str1Lent=str1.length; int str2Lent=str2.length; int[][] tempArray=new int[str1Lent][str2Lent]; int arrayLegth=Math.max(str1.length, str2.length); int[] beginMaxIndex=new int[arrayLegth]; //字符串起始坐标 int[] endMaxIndex=new int[arrayLegth]; //字符串介绍坐标 //例如 beginMaxIndex[0]=1,beginMaxIndex=3,代表str2的起始坐标的字串为1结束为3=dbb int maxLenth=0; int moveIndex=0;//相同长度的字符个数 for(int i=0;i<str1.length;i++){ for(int j=0;j<str2.length;j++){ if(str1[i]==str2[j]){ if(i==0||j==0){ tempArray[i][j]=1; }else{ tempArray[i][j]=tempArray[i-1][j-1]+1; } if(tempArray[i][j]>maxLenth){ maxLenth=tempArray[i][j]; endMaxIndex[0]=j+1; beginMaxIndex[0]=j+1-maxLenth; moveIndex=0; }else if(maxLenth==tempArray[i][j]){ moveIndex++; endMaxIndex[moveIndex]=j+1; beginMaxIndex[moveIndex]=j+1-maxLenth; } } } } if(moveIndex>0){ for(int i=0;i<moveIndex;i++){ int start=beginMaxIndex[i]; int end=endMaxIndex[i]; for(int j=start;j<end;j++){ System.out.print(str2[j]); } System.out.println(""); } } } public static void main(String[] args) { String str1 = new String("adbba1234"); String str2 = new String("adbbf1234sa"); getLCString(str1.toCharArray(),str2.toCharArray()); }
这样做速度比较快,但是花的空间太多。我们注意到在改进的矩阵生成方式当中,每生成一行,前面的那一行就已经没有用了。因此我们只需使用一维数组即可。最终的代码如下:
package lz77decompress; public class LCString2 { public static void getLCString(char[] str1, char[] str2) { int i,j; int len1,len2; len1 = str1.length; len2 = str2.length; int maxLen = len1 > len2?len1:len2; int[] max = new int[maxLen]; int[] maxIndex = new int[maxLen]; int[] c = new int[maxLen]; for (i = 0; i < len2 ; i++) { for (j = len1 -1; j >= 0; j--) { if (str2[i] == str1[j]) { if ( ( i == 0) || (j == 0) ) c[j] = 1; else c[j] = c[j-1] + 1; } else { c[j] = 0; } if (c[j] > max[0]) { //如果是大于那暂时只有一个是最长的,而且要把后面的清0; max[0] = c[j]; maxIndex[0] = j; for (int k = 1; k < maxLen; k++) { max[k] = 0; maxIndex[k] = 0; } } else if (c[j] == max[0]) { //有多个是相同长度的子串 for (int k = 1; k < maxLen; k++) { if (max[k] == 0) { max[k] = c[j]; maxIndex[k] = j; break; //在后面加一个就要退出循环了 } } } } } for (j = 0; j < maxLen; j++) { if (max[j] > 0) { System.out.println("第" + (j + 1) + "个公共子串:"); for (i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++) System.out.print(str1[i]); System.out.println(" "); } } } public static void main(String[] args) { String str1 = new String("adbba1234"); String str2 = new String("adbbf1234sa"); getLCString(str1.toCharArray(),str2.toCharArray()); } }