动态规划——能否合成字符串

前言

为什么写这篇博文

我相信每一个程序猿都梦想过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增加,所以,动态规划几乎是唯一解法,也是动态规划的魅力所在:不能确定过程中的子项到底与目标中的哪一位匹配(即匹配的不确定性),所以,就采用遍历的方式,每一种匹配方式都不放过。但要保证过程中的每一个子项的解都是当前的最优解。说白了,动态规划就是解决最优值的方法,只要保证过程是最优的,那么结果也一定可以是最优的。
后面我会再给出几个匹配不确定性的,使用动态规划的题目,方便一起总结。

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