字符串的全排列和所有组合问题及打靶问题

转自:https://blog.csdn.net/a1937935900/article/details/77103955

问题1 :输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

    思路:这是个递归求解的问题。递归算法有四个特性:(1)必须有可达到的终止条件,否则程序将陷入死循环;(2)子问题在规模上比原问题小;(3)子问题可通过再次递归调用求解;(4)子问题的解应能组合成整个问题的解。

    对于字符串的排列问题。如果能生成n - 1个元素的全排列,就能生成n个元素的全排列。对于只有1个元素的集合,可以直接生成全排列。全排列的递归终止条件很明确,只有1个元素时。下面这个图很清楚的给出了递归的过程。

字符串的全排列和所有组合问题及打靶问题_第1张图片

    参考代码:解法1通过Permutation_Solution1(str, 0, n); 解法2通过调用Permutation_Solution2(str, str)来求解问题。

 1 //函数功能 : 求一个字符串某个区间内字符的全排列
 2 //函数参数 : pStr为字符串,begin和end表示区间
 3 //返回值 :   无
 4 void Permutation_Solution1(char *pStr, int begin, int end)
 5 {
 6     if(begin == end - 1) //只剩一个元素
 7     {
 8         for(int i = 0; i < end; i++) //打印
 9             cout<

问题2:输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。

    思路:同样是用递归求解。可以考虑求长度为n的字符串中m个字符的组合,设为C(n,m)。原问题的解即为C(n, 1), C(n, 2),...C(n, n)的总和。对于求C(n, m),从第一个字符开始扫描,每个字符有两种情况,要么被选中,要么不被选中,如果被选中,递归求解C(n-1, m-1)。如果未被选中,递归求解C(n-1, m)。不管哪种方式,n的值都会减少,递归的终止条件n=0或m=0。

 1 //函数功能 : 从一个字符串中选m个元素
 2 //函数参数 : pStr为字符串, m为选的元素个数, result为选中的
 3 //返回值 :   无
 4 void Combination_m(char *pStr, int m, vector &result)
 5 {
 6     if(pStr == NULL || (*pStr == '\0'&& m != 0))
 7         return;
 8     if(m == 0) //递归终止条件
 9     {
10         for(unsigned i = 0; i < result.size(); i++)
11             cout< result;
33         Combination_m(pStr, i, result);
34     }
35 }
输入:[1,2,3]

输出:[[3],[1],[2],[1,2,3],[1,3],[2,3],[1,2],[]]

 

class Solution {
public:
    vector> subsets(vector& nums) {
        //这里排序并不必须,只是为了子集中元素以递增排序
        sort(nums.begin(), nums.end());
        vector> subs;
        vector sub;  
        genSubsets(nums, 0, sub, subs);
        return subs; 
    }
    void genSubsets(vector& nums, int start, vector& sub, vector>& subs) {
        //我们发现这里好像并没有递归出口,因为终止条件由下面for循环控制
        subs.push_back(sub);        
        for (int i = start; i < nums.size(); i++) {
            //情况一:子集包含元素nums[i]
            sub.push_back(nums[i]);
            //为何是i+1,因为已经解决了第i个元素,需要递归从第i+1个元素开始求解
            genSubsets(nums, i + 1, sub, subs);
            //情况二:子集不包含nums[i],即略过第i个元素
            //可以想象,不去管上一条递归语句,当下一次循环到i+1时,第i个元素已经略过
            sub.pop_back();
        }
    }
};

https://blog.csdn.net/qq_21997625/article/details/88411709 

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

若可以重复,则从i开始,不需要重复,从i+1开始

class Solution {
public:
    void dfs(int n,int k,int cur,vector out,vector> & res)
    {
        if(out.size()==k)
        {
            res.push_back(out);
            return;
        }
        for(int i=cur;i<=n;++i)
        {
            out.push_back(i);
            dfs(n,k,i+1,out,res);
            out.pop_back();
        }
    }
    vector> combine(int n, int k) {
        vector> res;
        dfs(n,k,1,{},res);
        return res;
        
    }
};

问题3:打靶问题。一个射击运动员打靶,靶一共有10环,连开10 枪打中90环的可能性有多少?

     思路:这道题的思路与字符串的组合很像,用递归解决。一次射击有11种可能,命中1环至10环,或脱靶。

     参考代码:

 1 //函数功能 : 求解number次打中sum环的种数
 2 //函数参数 : number为打靶次数,sum为需要命中的环数,result用来保存中间结果,total记录种数 
 3 //返回值 :   无
 4 void ShootProblem_Solution1(int number, int sum, vector &result, int *total)
 5 {
 6     if(sum < 0 || number * 10 < sum) //加number * 10 < sum非常重要,它可以减少大量的递归,类似剪枝操作
 7         return;
 8     if(number == 1) //最后一枪
 9     {
10         if(sum <= 10) //如果剩余环数小于10,只要最后一枪打sum环就可以了
11         {
12             for(unsigned i = 0; i < result.size(); i++)
13                 cout< result;
33     ShootProblem_Solution1(number, sum, result, &total);
34     cout<<"total nums = "<

 

你可能感兴趣的:(字符串全排列,字符串组合,打靶问题,数据结构/算法/刷题)