zoukankan      html  css  js  c++  java
  • 动态规划求解所有字符的组合数

    一,问题描述

    给定若干个字符,求解 这些字符能够表示的最多组合个数。比如{'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/

  • 相关阅读:
    tableView小细节
    iOS5 切换中文键盘时覆盖输入框的解决方案
    NSBundle读取txt文件,图片,plist
    iOS OC 字符串处理
    图片拉伸 几种方式
    UIAlertView小结
    新来报道
    VC6.0之Debug调试总结
    关于C++中的临时对象问题
    与临时对象的斗争(下)
  • 原文地址:https://www.cnblogs.com/hapjin/p/5583577.html
Copyright © 2011-2022 走看看