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 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());
}
}