求解最大子序列、最長遞增子序列、最長公共子串 、最長公共子序列
最大子序列:找出由數組成的一維數組中和最大的連續子序列。
最長遞增子序列(Longest Increasing Subsequence):設L=<a1,a2,…,an>是n個不同的實數的序列,L的遞增子序列是這樣一個子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<=ak2<=…<=akm。求最大的m值。
最長公共子串LCS:找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續的。
最長公共子序列:最長公共子序列與最長公共子串的區別在於最長公共子序列不要求在原字符串中是連續的。
Java代碼:
public class Subsequence {
public static int count;
public static void main(String[] args) {
testmaxSubsequence();testLongestIncreasingSubsequence();
testlongestCommonString();
testlongestCommonSubsequence();
}
// 最大子序列是要找出由數組成的一維數組中和最大的連續子序列。
// 比如{5,-3,4,2}的最大子序列就是 {5,-3,4,2},它的和是8,達到最大;
// 而{5,-6,4,2}的最大子序列是{4,2},它的和是6。
// 求解方法:創建一個同樣大小的數組,數組中的每一個位置記錄該位置前面的最大的連續子序列和,
// 該位置是否要繼續前面的最大的連續子序列就要看該位置前面的最大的連續子序列和是否大於0,
// 如果大於等於0,則加入該位置前面的最大的連續子序列,該位置值爲前面的最大的連續子序列和加上該位置對應的數組值,
// 如果小於0,則不加入該位置前面的最大的連續子序列,以自己爲新的最大的連續子序列,該位置值爲該位置對應的數組值。
public static void maxSubsequence(int[] array) {
if (array.length <= 0)
return;
int[] sum =newint[array.length];
int maxSum = array[0];
for (int i = 0; i < array.length; i++) {
if (i == 0)
sum[i] = array[i];
else {
if (sum[i - 1] >= 0)
sum[i] = sum[i - 1] +array[i];
else
sum[i] = array[i];
}
if (maxSum < sum[i])
maxSum = sum[i];
}
System.out.println("MaxSum:\t" + maxSum);
System.out.println("--------------------");
for (int i = 0; i < sum.length; i++) {
if (sum[i] == maxSum) {
int j = i;
while (j >= 0 && sum[j] >= 0)
j--;
System.out.print("MaxSubsequence:\t");
for (j++; j <= i; j++)
System.out.print(array[j] +" ");
System.out.println();
System.out.println("--------------------");
}
}
System.out.println("******************");
}
public static void testmaxSubsequence() {
int[] array;
array = new int[] { 5, -3, 4, 2 };
maxSubsequence(array);
array = new int[] { 5, -6, 4, 2 };
maxSubsequence(array);
array = new int[] { 5, -6, 4, 2, -10, 5, -6, 4, 2 };
maxSubsequence(array);
}
// 最長遞增子序列(Longest Increasing Subsequence)
// 設L=<a1,a2,…,an>是n個不同的實數的序列,L的遞增子序列是這樣一個子序列Lin=<aK1,ak2,…,akm>,
// 其中k1<k2<…<km且aK1<=ak2<=…<=akm。求最大的m值。
// 最長遞增子序列O(n^2)求解算法:
// 創建一個同樣大小的數組,用於記錄當前位置的值從頭開始到該位置對應的最長遞增子序列的長度
// 該數組中的值計算方法爲遍歷該位置左邊的所有記錄數值,找到最大的記錄數,當前位置值爲找到的最大記錄數加1.
// 如果要輸出最長遞增序列,需要另外創建一個同樣大小的數組,用於記錄構成當前位置數值對應最長遞增子序列的前面位置。
public static void longestIncreasingSubsequence(int[] array) {
if (array.length <= 0)
return;
// 記錄當前位置對應的最長遞增子序列的長度
int[] incLen =newint[array.length];
int maxLen;
// 遍歷數組
// 遍歷數值需要O(n),嵌套求當前位置前面的最長遞增子序列的長度需要O(n),故O(n^2)
for (int i = 0; i < array.length; i++) {
// 找到當前位置前面的最長遞增子序列的長度
maxLen = 0;
for (int j = 0; j < i; j++) {
if (maxLen < incLen[j] && array[i]>= array[j])
maxLen = incLen[j];
}
// 當前位置對應的最長遞增子序列的長度爲前面的最長遞增子序列的長度加1
incLen[i] = maxLen + 1;
}
// 遍歷記錄最長遞增子序列的長度數組,找到最長長度
maxLen = 0;
for (int i = 0; i < array.length; i++) {
if (maxLen < incLen[i])
maxLen = incLen[i];
}
// 打印出所有的最長遞增子序列。最長遞增子序列可能包含多個,用遞歸方法求解。
// 求解方法:最長長度的位置對應爲最長遞增子序列的最後一個位置,可能有多個。
// 遞歸求解已知最長遞增子序列位置的前一個位置,遍歷該位置左邊的所有位置,
// 長度等於當前長度減1並且數值小於等於當前數值的爲前一位置,可能有多個。
System.out.println("*******************");
System.out.println("Array(IncreaseLength)");
for (int i = 0; i < array.length; i++)
System.out.print(array[i] +"(" + incLen[i] +") ");
System.out.println();
System.out.println("LongestIncreasingSubsequence:\t");
System.out.println("MaxLength:\t" + maxLen);
System.out.println("------------------------");
int[] stack =newint[array.length];
int top = 0;
count = 0;
for (int i = 0; i < array.length; i++) {
if (maxLen == incLen[i]) {
stack[top++] = array[i];
printLonIncSubInfo(array,incLen, i, stack, top);
top--;
}
}
}
// 遞歸求解已知最長遞增子序列位置的前一個位置
public static void printLonIncSubInfo(int[] array,int[] incLen,int index,
int[] stack,int top) {
// 當最長遞增子序列對應位置長度爲1,則爲起點,輸出。可能含有多個起點
if (incLen[index] == 1) {
System.out.println("Num:\t" + (++count));
for (int i = top - 1; i >= 0; i--)
System.out.print(stack[i] +" ");
System.out.println();
System.out.println("------------------------");
}
for (int i = index - 1; i >= 0; i--) {
if (incLen[i] == incLen[index] - 1 &&array[index] >= array[i]) {
stack[top++] = array[i];
printLonIncSubInfo(array,incLen, i, stack, top);
top--;
}
}
}
// 最長遞增子序列O(nlogn)求解算法:
// 優化方法在尋找當前位置前面的最長長度,
// 記錄當前長度對應的最長遞增子序列的數值,當出現衝突,存儲最右邊的值。
// 採用二分查找的方法,將O(n)降到O(logn)
// 該優化算法只能最長遞增子序列的長度,無法得到最長遞增子序列。
public static void longestIncreasingSubsequenceOpt(int[] array) {
if (array.length <= 0)
return;
// 記錄當前長度對應的最長遞增子序列的數值
int[] lenVal =newint[array.length];
int lenCount;
lenCount = 0;
// 遍歷數組
// 遍歷數值需要O(n),嵌套求當前位置前面的最長遞增子序列的長度需要O(log(n)),故O(nlog(n))
for (int i = 0; i < array.length; i++) {
// 找到比當前值大並且最接近當前值的對應最長遞增子序列長度的位置
int sta = 0;
int end = lenCount - 1;
int mid;
while (sta < end) {
mid = sta + (end - sta) / 2;
if (array[i] > array[lenVal[mid]]) {
sta = mid + 1;
} elseif (array[i] < array[lenVal[mid]]) {
end = mid;
} else {
sta = mid + 1;
}
}
if (sta >= lenCount) {
lenVal[lenCount++] = i;
} else if (array[lenVal[sta]] > array[i]) {
lenVal[sta] = i;
if (sta == lenCount)
lenCount++;
} else {
lenVal[sta + 1] = i;
if (sta + 1 == lenCount)
lenCount++;
}
}
System.out.println("*******************");
System.out.println("LongestIncreasingSubsequence Length:\t" + lenCount);
System.out.println("*******************");
}
public static void testLongestIncreasingSubsequence() {
int[] array =newint[] { 1, -1, 3, 10, 5, 4, 7, 2, 9, 5, 5 };
longestIncreasingSubsequence(array);
longestIncreasingSubsequenceOpt(array);
}
// 最長公共子串LCS:
// 找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續的。
// 解法:將一個字符串作橫座標,一個字符串作縱座標,構成一個二維矩陣表,
// 矩陣中的值如果滿足橫座標值等於縱座標值爲true,否則爲false。
// 最長公共子串爲左上到右下的對角線最長的連續值爲true的解。
// 求解原理是錯位字符串,比較連續最長子串。
// 優化方法將矩陣設置成整型二維數組,在填充數值時加上右上角數值。
// 最長公共串爲二位數組中值最大的位置右上角到1的位置。
// 例如:求abcde和ebcda最長公共子串。
// e b c da
// a 0 0 00 1
// b 0 1 00 0
// c 0 0 20 0
// d 0 0 03 0
// e 1 0 00 0
public static void longestCommonString(String str1, Stringstr2) {
char[] array1 = str1.toCharArray();
char[] array2 = str2.toCharArray();
int[][] matrix =newint[array1.length][array2.length];
int maxLen = 0;
for (int i = 0; i < array1.length; i++) {
matrix[i] = newint[array2.length];
for (int j = 0; j < array2.length; j++) {
if (array1[i] == array2[j]) {
// 防止越界
if (i - 1 >= 0 && j - 1 >= 0)
matrix[i][j] = matrix[i- 1][j - 1] + 1;
else
matrix[i][j] = 1;
if (matrix[i][j] > maxLen)
maxLen = matrix[i][j];
} else
matrix[i][j] = 0;
}
}
// 輸出
int count = 0;
System.out.println("*******************");
System.out.println("longestCommonString");
System.out.println("String1:\t" + str1);
System.out.println("String2:\t" + str2);
System.out.println("-------------------");
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array2.length; j++) {
if (maxLen == matrix[i][j]) {
System.out.println("Num:\t" + (++count));
System.out
.println("String1 Location:\t" + (i - maxLen + 1));
System.out
.println("String2 Location:\t" + (j - maxLen + 1));
for (int k = maxLen - 1; k >= 0; k--)
System.out.print(array1[i -k]);
System.out.println();
System.out.println("-------------------");
}
}
}
}
public static void testlongestCommonString() {
longestCommonString("abcdefgabc","abc");
longestCommonString("abcdeabd","abccdeae");
longestCommonString("abcde","ebcda");
}
// 最長公共子序列
// 最長公共子序列與最長公共子串的區別在於最長公共子序列不要求在原字符串中是連續的,
// 比如ADE和ABCDE的最長公共子序列是ADE。
// 字符串編輯距離是同類問題。
// 動態規劃求解:
// C1是S1的最右側字符,C2是S2的最右側字符,S1‘是從S1中去除C1的部分,S2'是從S2中去除C2的部分。
//LCS(S1,S2)等於下列3項的最大者:
// (1)LCS(S1,S2’)
// (2)LCS(S1’,S2)
// (3)LCS(S1’,S2’)--如果C1不等於C2; LCS(S1',S2')+C1--如果C1等於C2;
// 邊界終止條件:如果S1和S2都是空串,則結果也是空串。
// gccctagcg和gcgcaatg求解:
// g c c c t a g c g
// g 1 1 1 1 1 1 1 1 1
// c 1 2 2 2 2 2 2 2 2
// g 1 2 2 2 2 2 3 3 3
// c 1 2 3 3 3 3 3 4 4
// a 1 2 3 3 3 4 4 4 4
// a 1 2 3 3 3 4 4 4 4
// t 1 2 3 3 4 4 4 4 4
// g 1 2 3 3 4 4 5 5 5
public static void longestCommonSubsequence(String str1, Stringstr2) {
if (str1.length() == 0 || str2.length() == 0)
return;
char[] array1 = str1.toCharArray();
char[] array2 = str2.toCharArray();
int[][] matrix =newint[array1.length][array2.length];
int max, maxLcs;
maxLcs = 0;
for (int i = 0; i < array1.length; i++) {
matrix[i] = newint[array2.length];
for (int j = 0; j < array2.length; j++) {
max = 0;
if (i - 1 >= 0 && matrix[i - 1][j]> max) {
max = matrix[i - 1][j];
}
if (j - 1 >= 0 && matrix[i][j - 1]> max) {
max = matrix[i][j - 1];
}
if (array1[i] == array2[j]) {
if (i - 1 >= 0 && j - 1 >= 0
&& matrix[i- 1][j - 1] + 1 > max)
max = matrix[i - 1][j -1] + 1;
elseif (1 > max)
max = 1;
} else {
if (i - 1 >= 0 && j - 1 >= 0
&& matrix[i- 1][j - 1] + 1 > max)
max = matrix[i - 1][j -1];
elseif (0 > max)
max = 0;
}
matrix[i][j] = max;
if (maxLcs > max)
max = maxLcs;
}
}
count=0;
System.out.println("*******************");
System.out.println("longestCommonSubsequence");
System.out.println("String1:\t" + str1);
System.out.println("String2:\t" + str2);
System.out.print(" ");
for (int j = 0; j < array2.length; j++) {
System.out.print(array2[j] +" ");
}
System.out.println();
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] +" ");
for (int j = 0; j < array2.length; j++) {
System.out.print(matrix[i][j]+" ");
}
System.out.println();
}
System.out.println("-------------------");
char[] stack =newchar[array1.length > array2.length ? array1.length
: array2.length];
printLongestCommonSubsequence(array1,array2, matrix,
array1.length - 1, array2.length - 1, stack, 0);
System.out.println("*******************");
}
public static void printLongestCommonSubsequence(char[] array1,
char[] array2,int[][] matrix,int i,int j,char[] stack,int top) {
if (matrix[i][j] == 1 && array1[i] ==array2[j]) {
stack[top++] = array1[i];
System.out.println("Num:\t" + (++count));
for (int k = top - 1; k >= 0; k--)
System.out.print(stack[k]);
System.out.println();
System.out.println("-------------------");
top--;
}
if (i - 1 >= 0 && j - 1 >= 0) {
if (array1[i] == array2[j]) {
stack[top++] = array1[i];
printLongestCommonSubsequence(array1,array2, matrix, i - 1,
j - 1, stack, top);
top--;
} else if (matrix[i - 1][j - 1] == matrix[i][j]) {
printLongestCommonSubsequence(array1,array2, matrix, i - 1,
j - 1, stack, top);
}
}
if (i - 1 >= 0 && matrix[i - 1][j]== matrix[i][j]) {
printLongestCommonSubsequence(array1,array2, matrix, i - 1, j,
stack, top);
}
if (j - 1 >= 0 && matrix[i][j - 1]== matrix[i][j]) {
printLongestCommonSubsequence(array1,array2, matrix, i, j - 1,
stack, top);
}
}
public static void testlongestCommonSubsequence() {
longestCommonSubsequence("gcgcaatg","gccctagcg");
longestCommonSubsequence("abcdef","ghijkl");
longestCommonSubsequence("abcdef","adefbc");
}
}
下載地址: