zoukankan      html  css  js  c++  java
  • 排列组合

    参考

    https://www.cnblogs.com/graphics/archive/2011/07/14/2105195.html

    https://zhuanlan.zhihu.com/p/79863106

    https://zh.wikipedia.org/wiki/%E7%BB%84%E5%90%88%E6%95%B0%E5%AD%A6

    我们在做算法题的时候,很多时候需要用到穷举,排列组合,获得所需的结果。然后再根据这个增加条件,慢慢减少判断次数,就是动态规划。所以我们先弄清楚如何排列组合。

    给定字符串s,求出s所有可能的子串。

    int main()
    {
        string a = "abcdef";
        string b = "acdfg";
        string c = "abc";
        int n = 1 << c.size();
        for (int i = 0; i < n; i++)
        {
            int j = i;
            string tmp;
            for (auto iter = c.rbegin(); iter != c.rend(); iter++)
            {
                if (j & 1)
                {
                    tmp = *iter + tmp;
                }
                j = j >> 1;
            }
            cout << tmp << endl;
        }
        char inchar;
        cin >> inchar;
    }

    这个解法,就是根据每一个元素,都有两种状态,在子串中,或是不在子串中。那么一个n个字符串,就会有2^n个组合。用一个二进制进行判断,每次右移1,个位置上是1的表示这次存在于子串。只不过这是倒叙的,需要反过来。

    这里的局限性就是,只能保存int的32位的字符串,如果是64位,也只能保存64个字符的字符串。再长的字符串就不行了,但是我们考虑到64个字符的字符串的所有组合情况,已经是天文数字了,在实际情况中,我们也不会碰到。碰到之后肯定有其他的解法。

    问题扩展,从n个元素中,选长度是m(m<=n)子串的组合。按照大家总结的规则,c(n,m)表示从n个字符串中取m个长度所有的子串情况,那么c(n,m)=c(n-1,m) + c(n-1, m-1)。解释就是当我要判断第n个元素加进来的子串情况时,只需要知道前n-1个子串的所有情况,再加上当前子串的情况,就可以得出最后的答案。前面n-1子串的情况,可以分为,第n个计算在内,那么就是c(n-1, m-1)和第n个不计算在内就是c(n-1,m)。

    比如abc,那么选择c(3,2)=c(3-1,2) + c(3-1, 2-1)。那么就是ab和a b,然后ab是不需要加c,a和b需要加c,就是ab ac bc。也就是计算第n个情况的时候,肯定需要先计算出n-1的情况,n-1的情况中包含了m个数,n就不用计算在内;包含了m-1的情况,就把m-1所有的情况都加上第n个元素。

    struct MNODE
    {
        string substr;
        MNODE* pnext;
        MNODE()
        {
            pnext = nullptr;
        }
    };
    MNODE* msubstr(string& srcstr, int index, int sublen)
    {
        MNODE* tmpp = nullptr;
        MNODE* prep = nullptr;
        if (index >= sublen && sublen > 0)
        {
            if (sublen == 1)
            {
                for (int i = 0; i < index; i++)
                {
                    MNODE* ftpn = nullptr;
                    if (tmpp == nullptr)
                    {
                        tmpp = new MNODE;
                        prep = tmpp;
                        ftpn = tmpp;
                    }
                    else
                    {
                        ftpn = new MNODE;
                        prep->pnext = ftpn;
                        prep = ftpn;
                    }
                    ftpn->substr = srcstr[i];
                }
            }
            else if (sublen > 1)
            {
                MNODE* nsub = msubstr(srcstr, index - 1, sublen - 1);
                tmpp = nsub;
                while (nsub != nullptr)
                {
                    nsub->substr = nsub->substr + srcstr[index - 1];
                    prep = nsub;
                    nsub = nsub->pnext;
                }
                nsub = msubstr(srcstr, index - 1, sublen);
                prep->pnext = nsub;
                while (nsub != nullptr)
                {
                    nsub = nsub->pnext;
                }
            }
        }
        return tmpp;
    }
    int main()
    {
        string a = "abcdef";
        string b = "acdfg";
        string c = "abcd";
        MNODE* psubstr = msubstr(a, a.size(), 3);
        while (psubstr != nullptr)
        {
            cout << psubstr->substr << endl;
            MNODE* tmpp = psubstr;
            psubstr = tmpp->pnext;
            delete tmpp;
        }
        char inchar;
        cin >> inchar;
    }

    按照公式推导,遇到索取的子字符串是1的时候,达到边界,可以求值退出,后面在前面的基础上增加当前的字符,不断累加,到最后得出结果。

    排列就是从n中选k,相同的元素,顺序不同,也算不同的一种,数学中排列的公式是

    如果选取的k个元素是可以重复的,那么就是,因为每次选择都是n个元素,可以选k次,所以就是n*n*n...*n,k个n种选择相乘。

    组合就是选择的元素,位置不同没有影响,比如从一堆球中选择不同颜色球的组合,数学中的组合公式是

    与排列类似,因为肯定还是需要取那么多种类,只不过要把重复的去掉,就再除以k的阶。

    如果是选取的元素可以重复,组合的公式就是需要额外加上重复的选择。这个公式可以转换成另一个

    那我们上面的2^n是如何得到的呢?来源于这个公式,我们可以知道,获取子串,虽然与顺序有关,但是,我们不能倒序获取或是从中间任意一个位置获取,字符串是有顺序的,获取的其他顺序的字符串不能算是子串。也就相当于获取固定索引位置的字符串,只能按照原来的顺序组成一个子串,不可能出现多个,也就是组合,按照这个公式,就是2^n。

  • 相关阅读:
    SEO优化技巧:16个方法优化网页中的图片
    《Google官方SEO指南》十一:以恰当的方式推广你的网站
    ASP.NET Eval如何进行数据绑定
    浅析ASP.NET页面缓存的几点体会
    浅谈ADO.NET中的五个主要对象
    WCF 入门 WCF基础知识问与答
    Hibernate第一次课(1)
    谷歌、雅虎支持中文域名搜索 有助提升SEO
    告诉SEO初学者:百度收录并非终极目标
    WF 创建 SQL 持久性数据库
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/12132458.html
Copyright © 2011-2022 走看看