動態規劃 || 最長子序列

相同子序列的定義

有兩個字符串S1和S2,在從左往右的順序中,有相同元素組成的序列,稱爲相同序列。必須強調的是:序列是不要求元素連續的。以下圖所示:算法

Spring和Spend的子序列是:SPN數組

 

 

 

解決思路

經過動態規劃來完成題目。首先,要使用動態規劃要明確轉移方程是什麼,而轉移方程須要對問題的理解和抽象才能得出。通常來講,動態規劃也須要把問題分解爲子問題,但不一樣於遞歸的分解過程是互不影響的,動態規劃每每須要前面的結果來決定後面的策略。同時,爲了減小沒必要要的計算,因此一般須要一個記錄的容器來記錄子問題的最優解,從而擴展爲全局最優解。ui

從題目上看:spa

  1. 第一種狀況:若當前的字符串S1與S2的最後一個元素相同,則能夠分解爲:S1的前m-1的元素與S2的前n-1個元素的最長子序列+1。
  2. 第二種狀況:若當前的字符串S1與S2的最後一個元素不相同,則能夠分解爲:比較S1的前m-1的元素與S2 或者 S1與S2的前n-1個元素之間的較大值。以下圖所示:

 

 

 

 所以,能夠得出如下的的轉移方程:3d

 其中:C是存儲子結果的數組,i和j分別是字符串S1和S2的下標。code

過程解析

下面經過圖解案例的方式來講明:對象

如圖所示,在算法還沒開始的狀態,能夠得知當字符串S1或S2的元素個數爲0時,他們的最長子序列必定是0。blog

 

接下來,從以Spend爲參照對象,以Spring爲比較對象,從Spring開始填表。遞歸

 

 從表中看出,Spring的第一個元素「S」與比較對象Spend的第一個元素相等,套用轉移方程:C[i-1][j-1]+1,即c[1][1]=0+1。索引

而元素「S「與Spend的後面元素比較都不相等,則套用轉移方程:c[1][2] = Max(C[i-1][j],C[i][j-1])即max(0,1),因此c[1][2]仍是等於1。

還有一個特殊的點是c[2][2],從上表能夠得出,S1[2]和S2[2]的元素都爲」P「,因此知足條件S1[i]==S2[j],因此能夠得出c[2][2]的值爲:c[1][1]+1 = 2。

根據上面的解析,能夠把表填充完整以下圖所示:

 

 

 那麼咱們要求的最長子序列的長度就是c[m][n],也就是二維數組的右下角的值

 而具體的代碼我也在下面貼出

 1 public class Solution {
 2 
 3     public static void main(String[] args) {
 4         String str1 = "Spring";
 5         String str2 = "Spend";
 6         char[] s1 = str1.toCharArray();
 7         char[] s2 = str2.toCharArray();
 8 
 9         //設定一個二維數組存放當前的最長公共子序列,如前i個s1元素與前j個s2元素有多少個公共子序列元素
10         int[][] c = new int[s1.length+1][s2.length+1];
11         /*
12          * 根據轉移方程:若s1[i]==s2[i]
13          * c[i][j] = c [i-1][j-1]+1
14          * 若s1[i]!=s2[i]
15          * c[i][j] = max(c[i][j-1] , c[i-1][j]  )
16         **/
17         for (int i =1;i<=s1.length;i++){
18             for (int j=1;j<=s2.length;j++){
19                 if (s1[i-1] == s2[j-1]) {
20                     c[i][j] = c [i-1][j-1]+1;
21                 }else {
22                     c[i][j] = Math.max(c[i][j-1] , c[i-1][j] );
23                 }
24             }
25         }
26     }
27 }

 

求出一個最長子序列

在上面已經求得存放比較結果的數組,若是題目要求輸出一個最長子序列,那麼能夠根據這個數組來還原。

還原的策略以下:

  1. 當前元素相等,保存元素,同時縮減i和j
  2.  元素不相等,往c值較大的方向縮減,若左邊和上方的值相等,則往上邊縮減(也能夠往左邊縮減)。
  3. 當字符串的索引i或者j到達0時,中止縮減。

如圖所示:

 

完整的代碼以下

public class Solution {

    public static void main(String[] args) {
        String str1 = "Spring";
        String str2 = "Spend";
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();

        //設定一個二維數組存放當前的最長公共子序列,如前i個s1元素與前j個s2元素有多少個公共子序列元素
        int[][] c = new int[s1.length+1][s2.length+1];
        /*
         * 根據轉移方程:若s1[i]==s2[i]
         * c[i][j] = c [i-1][j-1]+1
         * 若s1[i]!=s2[i]
         * c[i][j] = max(c[i][j-1] , c[i-1][j]  )
        **/
        for (int i =1;i<=s1.length;i++){
            for (int j=1;j<=s2.length;j++){
                if (s1[i-1] == s2[j-1]) {
                    c[i][j] = c [i-1][j-1]+1;
                }else {
                    c[i][j] = Math.max(c[i][j-1] , c[i-1][j] );
                }
            }
        }

        //根據數組倒推出子序列
        StringBuilder result =new StringBuilder();
        int i = s1.length;
        int j = s2.length;
        while (i>0 && j>0){
            //當前元素相等,保存元素,同時縮減i和j
            if (s1[i-1] == s2[j-1]){
                result.insert(0,s1[i-1]);
                i--;j--;

            }else{
                //元素不相等,往c值較大的方向縮減,若左邊和上方的值相等,則往上邊縮減。
                if (c[i-1][j] >= c[i][j-1]){
                    i--;
                }else {
                    j--;
                }
            }

        }
        System.out.println(result.toString());
    }
}
相關文章
相關標籤/搜索