1. 程式人生 > >最長公共子字串的java語言求解及記憶體優化

最長公共子字串的java語言求解及記憶體優化

動態規劃的本質是以空間換時間:把一個問題劃分成許多子問題,如果這些子問題會被計算很多次,那就把它們的計算結果儲存起來以節省計算時間。比如斐波那契數列的公式F(n) = F(n-1) + F(n-2),其中F(n-1)F(n-2)的結果會被計算很多次,所以可以使用一個數組儲存對應的結果a[i] = F(i)

動態規劃問題求解的一般步驟是先劃分子問題、找出狀態轉移方程、找出邊界條件然後計算。在最長公共子字串這個問題中,子問題可以如此劃分:

令L(i,j)表示字串s1[0..i]和字串s2[0..j]的以s1[i]和s2[j]結尾的最長公共子字串的長度,則

 1. 當s1[i] == s2[j]
時,L(i,j) = L(i-1,j-1)+12. 當s1[i] != s2[j]時,L(i,j) = 0

計算出所有的L(i,j),其中0<=i<s1.length,0<=j<s2.length,然後遍歷L矩陣找出最大值即可。

程式碼如下,其中找出最大值這一步和計算矩陣結果這一步可以合併,以減少迴圈次數:

public static int f(String s1, String s2) {
        int m = s1.length();
        int n = s2.length();
        char[] c1 = s1.toCharArray();
        char
[] c2 = s2.toCharArray(); int[][] dp = new int[m][n]; //初始化dp的第一行和第一列 for (int i = 0; i < m; i++) { if (c1[i] == c2[0]) dp[i][0] = 1; } for (int i = 0; i < n; i++) { if (c1[0] == c2[i]) dp[0][i] = 1; } //生成狀態轉移矩陣並找出最大值
int max = 0; for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { if (c1[i] == c2[j]) { int result = dp[i - 1][j - 1] + 1; dp[i][j] = result; max = max < result ? result : max; } } } return max; }

可以看到,在計算矩陣dp的第i行時,只用到了dp的第i-1行,所以只使用兩行陣列就可以計算出矩陣的值,這樣可以減少記憶體消耗。程式碼如下:

public static int fLessMem(String s1, String s2) {
        int m = s1.length();
        int n = s2.length();
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        int[][] dp = new int[2][n];

        for (int i = 0; i < n; i++) {
            if (c1[0] == c2[i]) {
                dp[1][i] = 1;
            }
        }

        int max = 0;
        int cur = 1;  //指明dp矩陣的哪一行是正在計算的行
        int data = 0; //指明dp矩陣的行是快取的行
        for (int i = 1; i < m; i++) {
            //更新快取行的第一個元素
            //由於現在計算的是第i行,所以data行對應的是第i-1行的元素
            dp[data][0] = c1[i - 1] == c2[0] ? 1 : 0;
            for (int j = 1; j < n; j++) {
                if (c1[i] == c2[j]) {
                    int result = dp[data][j - 1] + 1;
                    dp[cur][j] = result;
                    max = max < result ? result : max;
                }
            }
            //交換正在計算的行和快取的資料行
            cur ^= data;
            data ^= cur;
            cur ^= data;
        }
        return max;
    }