動態規划求解所有字符的組合數


一,問題描述

給定若干個字符,求解 這些字符能夠表示的最多組合個數。比如{'a','b','c'} 一共有七種組合。(每種組合沒有重復的字符 且 組合的種數與順序無關,如 ab 和 ba 是同一種組合)

a、b 、c 、ab 、ac 、bc 、abc

其實,求組合個數,可以用公式來求解:具給定 n種字符,一共有  c(n,1)+c(n,2)+...+c(n,n)種不同的組合。其中,c(n,i)表示:從n個字符中任選 i 個的組合數,數學表示為:C in

 

二,DP算法思路

既然已經可以用上面的公式 c(n,1)+c(n,2)+...+c(n,n) 來計算不同的組合數了,那為什么還用DP?因為,上面的公式只能給定組合數,但是不能給出具體是哪些組合。

假設輸入 m 個字符(互不相同),則這些字符只能構成長度為 1,2,....m 的組合,設某個組合的長度為 n。即: 1 =< n <= m

設 c[m][n] 表示 使用 m 種不同的字符,表示長度最多為 n 的組合個數 的最大值

最大值 體現了最優子問題性質。最優子問題分析如下:把字符組合分成兩部分,第一個字符 以及剩下的所有字符。

對於第 m 種字符而言,只有兩種情況:①是字符組合的第一個字符,②不是在字符組合的第一個字符。因此,可以應用《組合數學》中的加法原理。(比如 'abc' 就是一個字符組合)

c[m][n]=c[m-1][n-1] + c[m-1][n]

c[m-1][n-1],表示第 m 個字符字符組合中的第一個字符。此時,該問題變成:用 1,2,...m-1個字符(共 m-1 種) 來 組合 長度為 n-1的 字符組合(‘字符串’)

c[m-1][n],表示第 m 個字符不是 字符組合中的第一個字符。此時,該問題變成:在剩余的 m-1種字符里面選出 n 個字符來組合。???(有點不太對)

'm' ? ? .... ?             第一個字符是 'm'

'*'  ? ? .....?             第一個字符不是 'm'

 

注意,原問題表示的字符組合 長度是 n

 

這個問題的DP求解與 動態規划求解最多有幾種方案求解硬幣找零問題 非常相似。

 同時,這也是一個典型的加法原理的應用。

 

三,代碼實現

public class AllComposite {
    
    /**
     * 
     * @param str 存儲可用的字符種類
     * @param m 可用的字符種類數
     * @param n 組合中的最大長度
     * @return  
     */
    public static int allCombination(char[] str, int m, int n){
        
        //base condition
        if(m == 0 && n > 0)
            return 1;
        
        if(n == 0)
            return 0;
        
        return allCombination(str, m-1, n-1)+ allCombination(str, m-1, n);
    }
    
    
    public static int dpAllCombination(char[] str, int n){
        int[][] c = new int[n+1][n+1];
        
        
        //可根據上面的遞歸來判斷初始條件
        for(int i = 0; i <=n; i++)
        {
            c[0][i] = 1;
            c[i][0] = 0;//c[0][0]=0
        }
        
        //c[n][n]=c[n-1][m-1] + c[n-1][m]
        for(int i = 1; i <=n; i++)
        {
            for(int j = 1; j <=n; j++)
            {
                c[i][j] = c[i-1][j-1] + c[i-1][j];
            }
        }
        return c[n][n];
    }
    
    public static void main(String[] args) {
        char[] str = {'a','b','c','d'};
        int m ,n ;
        m = n = str.length;
        int result = allCombination(str, m, n);
        int result2 = dpAllCombination(str, n);
        System.out.println(result);
        System.out.println(result2);
    }
}

對於初始條件的確定,可以先畫一個小的示例圖來確定。比如:allCombination({'a','b','c'}, 3)......

在代碼里面 記錄下具體選了哪些字符,就可以打印輸出具有的字符組合了。其實我也不會。

 

時間復雜度分析:從上面的遞歸方法來看:遞歸表達式為 T(m)=2T(m-1),得出:T(m)=2^m ,指數級復雜度

而從上面的動態規划求解方法來看:時間復雜度為O(m^2)

原因就是:這個問題存在重疊的子問題,對於DP而言,不需要重復地計算某些值,而是通過查找求得。這篇文章 求解兩個字符串的最長公共子序列 詳細分析了DP的優勢在哪里。

(動態規划的兩個基本特征:①最優子結構;②重疊子問題)

 

四,參考資料

http://www.cnblogs.com/hapjin/p/5579737.html

 求解兩個字符串的最長公共子序列

http://wuchong.me/blog/2014/07/28/permutation-and-combination-realize/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM