動態規劃——能否合成字符串

前言

爲什麼寫這篇博文

我相信每一個程序猿都夢想過gg這種大廠,所以,算法都是必不可少的。
目前刷的所有題目裏面,其中動態規劃是最難的一種了。想運用好動態規劃,不是一件簡單的事,動態規劃有兩個點很重要:
1.結果能否拆分成子項,或者說,結果是否有前一個過程完全決定。
2.動態轉移方程。
下面看幾個實際的例子。

題目

題意

這個題目是這樣的,給定兩個字符串s1和s2,再給定一個字符串s3,問由前兩個字符串能否得到第三個字符串。
比如s1 = “abcg”,s2 = “pbad”,s3 = “apbacaqd” 返回false;s1 = “abcg”,s2 = “pbad”,s3 = "apbcbagd"返回true;s1 = “abcg”,s2 = “pbad”,s3 = "abcgpbad"返回true;

解析

這個題目咋看之下很懵逼,似乎只能用暴力來解決。
但是如果有人提示,這個題目可以使用動態規劃來做,似乎好很多。
首先,這個問題需要幾維度的數組來表示狀態呢?
一維?肯定不行,兩個字符串,如果用一維,只能表示當前匹配到哪個字符串,而沒法統計到使用a,b字符串的哪一個。
二維?dp[i][j],i表示使用a字符串第幾位,j表示使用j字符串第幾位,因爲i,j都有選取或者不選取。dp[i][j]表示當前a使用i個,b使用j個,能否匹配成功。
動態方程呢?
先說結論:
dp[i][j] = (ca == cc && dp[i - 1][j]) || (cb == cc && dp[i][j - 1]);
即,有兩種情況:
1.由a字符串的第 i 個來與c中的 ( i + j - 1 ) 匹配,並且滿足a的前 ( i - 1 ) 個字符和 c 的 ( (i-1) + j - 1 ) 匹配成功,
2.由b字符串的第j個來與c中的 ( i + j -1) 匹配,並且滿足b的前 (j-1) 個字符和c的 ( i+ (j-1) - 1 ) 匹配成功。
那麼,我是否需要關心,第 ==( i + j -1)==到底是和a b中的哪一個字符串匹配呢?
答:不需要,只要匹配成功就好,不需要關心到底和哪一個匹配。注意,這一點很重要,因爲有的題目是需要關心和第幾個匹配。

    private boolean kmss(String a, String b, String c) {
        boolean dp[][] = new boolean[a.length() + 1][b.length() + 1];
        for (int i = 0; i <= a.length(); i++) {

            for (int j = 0; j <= b.length(); j++) {
                char cb, ca, cc; //=
                //char cc = b.charAt(i - 1 + j);
                if (i == 0 && j == 0) {
                    dp[i][j] = true;
                    continue;
                } else if (i == 0) {
                    cc = c.charAt(i + j - 1);
                    cb = b.charAt(j - 1);
                    dp[i][j] = cb == cc && dp[i][j - 1];
                } else if (j == 0) {
                    ca = a.charAt(i - 1);
                    cc = c.charAt(i + j - 1);
                    dp[i][j] = cc == ca && dp[i - 1][j];
                } else {
                    cb = b.charAt(j - 1);
                    ca = a.charAt(i - 1);
                    cc = c.charAt(i + j - 1);
                    dp[i][j] = (ca == cc && dp[i - 1][j]) || (cb == cc && dp[i][j - 1]);
                }
                System.out.print(dp[i][j] + " ");
            }
            System.out.println();
        }
        for (int i = 0; i <= a.length(); i++) {
            for (int j = 0; j <= b.length(); j++) {

            }

        }
        return dp[a.length()][b.length()];
    }

好了,時間複雜度多少?O(nm)。
但是
在寫的過程中,我發現大家其實都應該發現了,在循環的時候,只要前面一個是false,後面的一定爲false,這麼說不準確,舉個例子你就明白了。
比如dp[1][2] = false,那麼dp[1][3],dp[1][4]也就沒有必要去判斷了,肯定爲false。但是,dp[2][1]和dp[2][2]還是有必要判斷下的。同時,如果dp[1][0]爲0,那麼dp[2,3,4,5,6,7][0]都沒必要判斷了
那麼,這個題目是否還有如下的解決方法?
代碼如下:

int ap = 0, bp = 0;
        while ((ap + bp) < c.length()) {
        	if(...) ap++;
        	if(...) bp++;
 		}       

很明顯,不能,這就是動態規劃的原因了,因爲你不確定a中的第i位到底對應c中的哪一位,以及是ap增加還是bp增加,所以,動態規劃幾乎是唯一解法,也是動態規劃的魅力所在:不能確定過程中的子項到底與目標中的哪一位匹配(即匹配的不確定性),所以,就採用遍歷的方式,每一種匹配方式都不放過。但要保證過程中的每一個子項的解都是當前的最優解。說白了,動態規劃就是解決最優值的方法,只要保證過程是最優的,那麼結果也一定可以是最優的。
後面我會再給出幾個匹配不確定性的,使用動態規劃的題目,方便一起總結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章