“最長上升子序列,最大連續子序列和,最長公共子串”的Java實現
一、問題描述
這是三道典型的dp問題。 最長上升子序列:在一列數中尋找一些數,這些數滿足:任意兩個數a[i]和a[j],若i<j,必有a[i]<a[j],這樣最長的子序列稱為最長遞增(上升)子序列。設dp[i]表示以i為結尾的最長遞增子序列的長度,則狀態轉移方程為:dp[i] = max{dp[j]+1}, 1<=j<i,a[j]<a[i].時間複雜度為O(n*n);
考慮兩個數a[x]和a[y],x<y且a[x]<a[y],且dp[x]=dp[y],那麼我們該選擇哪個呢?顯然a[x],因為它更有潛力,也就是說我們可以用a[x]來替換掉a[y],也就是說我們需要維護一個數據結構來儲存可能的遞增子序列的元素,並且需要在某些時候進行替換。因此我們可以用一個連結串列來儲存,並且在查詢替換位置的時候用二分查詢來實現,這樣時間複雜度為O(nlogn)。
最大連續子序列和:在一列數中尋找一些數,這些數滿足:任意兩個數a[i]和a[j],若i+1=j,必有a[i]<a[j],且∑()最大。需要明確的是狀態轉移方程中的狀態代表的含義。因為contiguous,所以dp[i]代表的應該以i位置元素結尾的連續值,並非最大值。
最長公共子串:兩個字串中的最常公共連續子串。
找 兩個字串的最長公共子串,這個子串要求在原字串中是連續的。其實這又是一個序貫決策問題,可以用動態規劃來求解。我們採用一個二維矩陣來記錄中間的結 果。這個二維矩陣怎麼構造呢?直接舉個例子吧:"bab"和"caba"(當然我們現在一眼就可以看出來最長公共子串是"ba"或"ab")
b a b
c 0 0 0
a 0 1 0
b 1 0 1
a 0 1 0
我們看矩陣的斜對角線最長的那個就能找出最長公共子串。
不過在二維矩陣上找最長的由1組成的斜對角線也是件麻煩費時的事,下面改進:當要在矩陣是填1時讓它等於其左上角元素加1。
b a b
c 0 0 0
a 0 1 0
b 1 0 2
a 0 2 0
這樣矩陣中的最大元素就是 最長公共子串的長度。
因此我們需要維護一個二維陣列,行數為第一個字串的長度,列數為第二個字串的長度。為了擷取子串,那麼我們需要子串的起始和結束索引。藉助之前的分析,因為dp裡記錄的是到某個節點的長度,因此我們可以通過維護一個長度和結束索引來變相的記錄子串的起始和結束索引。
二、Java 程式碼
/** * 最長遞增子序列 * * @param nums * @return */ public int getLIS(int[] nums) { if (nums==null || nums.length==0) { return 0; } int max = 1; int[] dp = new int[nums.length]; for (int i = 0; i < nums.length; i++) { dp[i] = 1; for (int j = 0; j < i; j++) { if (nums[j]<nums[i]) { dp[i] = Math.max(dp[i], dp[j]+1); max = Math.max(dp[i], max); } } } return max; } /** * 最長遞增子序列 * * @param nums * @return */ public int getLIS(int[] nums) { if (nums == null || nums.length == 0) { return 0; } ArrayList<Integer> dp = new ArrayList<>(); for (int item : nums) { if (dp.size() == 0 || dp.get(dp.size() - 1) < item) { dp.add(item); } else { int i = Collections.binarySearch(dp, item);//insert position dp.set(i < 0 ? -i - 1 : i, item); } } return dp.size(); } /** * 最大連續子序列和 * * @param nums * @return */ public int getMaxSubArray(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int maxEndingHere = 0; int maxSoFar = Integer.MIN_VALUE; for (int i = 0; i < nums.length; i++) { if (maxEndingHere < 0) { maxEndingHere = 0; } maxEndingHere += nums[i]; maxSoFar = Math.max(maxSoFar, maxEndingHere); } return maxSoFar; } /** * 最長公共子串 * * @param a * @param b * @return */ public String getLCS(String a, String b) { if (a == null || b == null || a.length() == 0 || b.length() == 0) { return null; } int[][] dp = new int[a.length()][b.length()]; int endHere = 0; int maxLen = 0; for (int i = 0; i < a.length(); i++) { for (int j = 0; j < b.length(); j++) { if (i == 0 || j == 0) { dp[i][j] = a.charAt(i) == b.charAt(j) ? 1 : 0; } else { dp[i][j] = a.charAt(i) == b.charAt(j) ? dp[i - 1][j - 1] + 1 : 0; } if (dp[i][j] > maxLen) { maxLen = dp[i][j]; endHere = i; } } } return a.substring(endHere - maxLen + 1, endHere + 1); }