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/

  • 相关阅读:
    10 种保护 Spring Boot 应用的绝佳方法
    Redis 如何分析慢查询操作?
    Spring Boot 主类及目录结构介绍
    Redis 再牛逼,也得设置密码!!
    Spring Data Redis 详解及实战一文搞定
    Spring Boot Redis Cluster 实战干货
    超详细的 Redis Cluster 官方集群搭建指南
    Redis Linux 安装运行实战全记录
    hdu 4790 Just Random (思路+分类计算+数学)
    poj 1328 Radar Installation(贪心)
  • 原文地址:https://www.cnblogs.com/hapjin/p/5583577.html
Copyright © 2011-2022 走看看