Subset full permutation and combination number problem (take you to easily grasp eleven OJ questions)

content

1. Subset

2. Subset II

3. Full arrangement

4. Full permutation II

5. Full arrangement of characters

6. String size full arrangement

7. Combined sum 

 8. Combined Sum II

9. Combined Sum III

10. Combined Sum IV

11. Incrementing subsequences


1. Subset

Sword Points Offer II 079. All Subsets - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

1. Violent enumeration is enough, that is, each element has two choices, one is to want, the other is not. We can enumerate them all: take the array [1,2,3] as an example:

To sum it up:

①Draw the recursion tree and find the state variables (parameters of the backtracking function), this step is very important ※
②According to the meaning of the question, establish the end condition
③Find the selection list (related to the function parameters), which is closely related to the first step ※
④Judgment Whether you need to prune
⑤ Make a selection, call recursively, and enter the next layer
⑥ Undo the selection

Corresponding process diagram:

Corresponding code:

class Solution {
public:
           vector<vector<int>>ans;//记录答案
    vector<vector<int>> subsets(vector<int>& nums) {
                vector<int>tmp;
                process(nums,0,tmp);
                return ans;
    }
    void process(vector<int>&nums,int index,vector<int>&tmp){
                    if(nums.size()==index){
                       ans.push_back(tmp);
                       return;
                    }
                    //不选择当前的数
                    process(nums,index+1,tmp);
                    
                    tmp.push_back(nums[index]);
                    //选择当前的数
                    process(nums,index+1,tmp);
                    tmp.pop_back();//递归回来的时侯删掉
    }
};

2. Subset II

90. Subset II - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The difference between this question and the previous question is that there are repeated elements in the array, so that when we select a number or a number is not selected, there may be duplicate subsets, and the question requires us not to have duplicate subsets, so here We need to prune, here is an example of an array [1,2,2,3]

 There are duplicate subsets here, so how to filter out duplicates, that is, two different branches at the same depth, if the current element is the same as the previous element, we skip it. As shown below.

Corresponding code:

class Solution {
public:
        vector<int>tmp;
        vector<vector<int>>ans;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());//注意一定要排序
        //方便去重
       process(nums,0,INT_MAX);
       return ans;
    }
    void process(vector<int>&nums,int index,int prev)
    //prev代表前一个已经被选择的元素
    {
        if(index==nums.size())//如果已经到了最后了
        {
            ans.push_back(tmp);
            return;
        }
        tmp.push_back(nums[index]);//选择当前的数
        process(nums,index+1,nums[index]);
        tmp.pop_back();//撤回选择
       if(nums[index]!=prev)//如果当前数和前一个选择的数不相等才进行下一个分支
        process(nums,index+1,prev);

    }
};

3. Full arrangement

46. ​​Full arrangement - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

Every element is in a position where it can be tried, and every position is tried. Each element can only try itself and the position behind itself. The following shows the full permutation of the array [1,2,3]

Corresponding code:

class Solution {
public:
           vector<vector<int>>ans;
    vector<vector<int>> permute(vector<int>& nums) {
          process(nums,0);
          return ans;
    }
    void process(vector<int>&nums,int i)
    {
        if(i==nums.size())//不能交互
        {
            ans.push_back(nums);
            return;
        }
        for(int j=i;j<nums.size();j++)
        {
            swap(nums[i],nums[j]);//来到当前位置
            process(nums,i+1);
            swap(nums[i],nums[j]);//撤销决定
        }
    }
    
};

4. Full permutation II

Corresponding letecode link:

47. Full Arrangement II - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The only difference between this question and the previous question is that there are duplicate elements in the array, so if we don't do any deduplication processing, there will definitely be a full array of duplicates. So we need to deduplicate, how to deduplicate? We only need to define a hash table to record the current position, and skip if the next position that has been reached has already been recorded in the hash table.

Please see the code for details:

class Solution {
public:
           vector<vector<int>>ans;//记录答案
    vector<vector<int>> permuteUnique(vector<int>& nums) {
       sort(nums.begin(),nums.end());//排序更容易去重不排序也行
              process(nums,0);
              return ans;
    }
    void process(vector<int>&nums,int i)
    {
        if(i==nums.size())
        {
            ans.push_back(nums);
        }
        unordered_set<int>Hash;//记录访问过了的位置
        for(int j=i;j<nums.size();j++)
        {
            if(!Hash.count(nums[j]))//查看上次来到的位置和这次来到位置是否相同
            {
                Hash.insert(nums[j]);
                swap(nums[i],nums[j]);
                process(nums,i+1);//去下一个位置
                swap(nums[i],nums[j]);//撤销决定
            }
        }
    }
    
};

5. Full arrangement of characters

Sword Points Offer 38. Arrangement of Strings - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

There is no difference between this question and the previous question except that one is a number and the other is a character. The idea has already been mentioned in the question, and only the code is given here.

Corresponding code:

class Solution {
public:
       vector<string>ans;
    vector<string> permutation(string s) {
           process(s,0);
           return ans;
    }
    void process( string&str,int i)
    {
        if(i==str.size())
        {
            ans.push_back(str);
        }
        vector<bool>isVisit(256,false);
        for(int j=i;j<str.size();j++)
        {
           if(!isVisit[str[j]-'a'])
           {
           isVisit[str[j]-'a']=true;
            swap(str[i],str[j]);
            process(str,i+1);
            swap(str[i],str[j]);
           }

           
        }
    }
};

6. String size full arrangement

784. Full Arrangement of Uppercase and Lowercase Letters - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

If each position is a letter, there are two options: 1. Unchanged, 2 becomes uppercase or lowercase. If it is not a character, it will go directly to the next position.

7. Combined sum 

39. Combined Sum - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

For the same two elements of this question, there are two options: select and not select, but each element of this question can be selected countless times, just follow the previous method:

①Draw the recursion tree and find the state variables (parameters of the backtracking function), this step is very important ※
②According to the meaning of the question, establish the end condition
③Find the selection list (related to the function parameters), which is closely related to the first step ※
④Judgment Whether you need to prune
⑤ Make a selection, call recursively, and enter the next layer
⑥ Undo the selection

In this question, you can sort the array first to facilitate pruning in the recursive process. Please see the code for details: 

Corresponding code:

class Solution {
public:
        vector<vector<int>>ans;//记录答案
        vector<int>tmp;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
           sort(candidates.begin(),candidates.end());//排序方便后序剪枝优化
           dfs(candidates,target,0);
           return ans;
    }
    void dfs(vector<int>&nums,int target,int index)
    {
        if(target==0)//是否已经找到答案了
        {
         ans.push_back(tmp);
         return;
        }
       
       for(int i=index;i<nums.size();i++)
       {
           if(nums[i]>target)//如果当前数大于target由于数组有序所以后面的数都是大于target
           //不可能在凑出target
           {
              return;
           }
           tmp.push_back(nums[i]);//选择当前的数
           dfs(nums,target-nums[i],i);//注意不是i+1而是i因为一个数可以选择多次
           tmp.pop_back();//撤销决定
       }
    }
};

 8. Combined Sum II

40. Combined Sum II - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

There is basically no change between this question and the previous question, but there is one more step to de-dupe:

Corresponding code:

class Solution {
public:
          vector<vector<int>>ans;
          vector<int>tmp;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
              process(candidates,target,0);
              
              return ans;
    }
    void process(vector<int>&nums,int target,int index)
    {
        if(target==0)
        {
           ans.push_back(tmp);
           return; 
        }
        for(int i=index;i<nums.size();i++)
        {
            if(i>index&&nums[i]==nums[i-1])//防止出现重复元素
            {
                continue;
            }
            if(nums[i]>target)//有序数组后面不可能搞出target了
            {
                return;
            }
            tmp.push_back(nums[i]);//选择当前的数
            process(nums,target-nums[i],i+1);
            tmp.pop_back();//撤销选择

        }
    }
};

9. Combined Sum III

216. Combined Sum III - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The problem-solving idea of ​​this question is basically the same as that of the previous question, and it is not necessary to repeat it. It is a weakened version of the previous question. Please see the code for details.

Corresponding code:

class Solution {
public:
  vector<int>tmp;
  vector<vector<int>>ans;
    vector<vector<int>> combinationSum3(int k, int n) {
        process(k,1,n);
        return ans;
    }
    void process(int k,int index,int taraget)
    {
        if(k==0&&taraget==0)//说明已经找到了
        {
            ans.push_back(tmp);
            return;
        }
        for(int i=index;i<=9;i++)//从index到9;
        {
            tmp.push_back(i);//选择当前的数
            process(k-1,i+1,taraget-i);
            tmp.pop_back();//撤回选择

        }
    }
};

10. Combined Sum IV

377. Combination Sum IV - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The problem-solving idea of ​​this problem is still violent recursion. The difference from the previous problem is that it can start from position 0 every time. Please see the code for details:

Corresponding code:

int combinationSum4(vector<int>& nums, int target) {
      
          return process(nums,target);
    }

    int process(vector<int>&nums,int target)
    {
        if(target<0)//如果target小于0那么没有方法数
        {
            return 0;
        }

        if(target==0)//到0了
        {
            return 1;
        }
        int ans=0;
        for(int i=0;i<nums.size();i++)
        {
            ans+=process(nums,target-nums[i]);//选择当前的数
        }
      
        return ans;//返回答案即可
    }

But it is very regrettable that this method directly times out due to a large number of repeated calculations:

There are two ways to solve this problem, one is memoized search, and the other is dynamic programming. Both methods are given below:

Memoized search: It is actually very simple. We can define a dp table to record the answer in advance before returning. If the answer is found to have been recorded, I can return directly without calling recursive repeated calculations:

class Solution {
public:
    vector<int>dp;
    int combinationSum4(vector<int>& nums, int target) {
      
            dp.resize(target+1);
          return Dp(nums,target);
    }

    int process(vector<int>&nums,int target)
    {
       
        if(target==0)
        {
            return 1;
        }
        if(target<0)
        {
            return 0;
        }
         if(dp[target]!=-1)//说明之前已经计算过了
        {
            return dp[target];
        }
        int ans=0;
        for(int i=0;i<nums.size();i++)
        {
            ans+=process(nums,target-nums[i]);
        }
        dp[target]=ans;//提前将答案记录一下
        return ans;//返回答案
    }

The dynamic programming version is to come over on the basis of recursion:

class Solution {
public:
    vector<int>dp;
    int combinationSum4(vector<int>& nums, int target) {
      
              dp.resize(target+1);
          return Dp(nums,target);
    }

    int process(vector<int>&nums,int target)
    {
       
        if(target==0)
        {
            return 1;
        }
        if(target<0)
        {
            return 0;
        }
         if(dp[target]!=-1)//说明之前已经计算过了
        {
            return dp[target];
        }
        int ans=0;
        for(int i=0;i<nums.size();i++)
        {
            ans+=process(nums,target-nums[i]);
        }
        dp[target]=ans;//提前将答案记录一下
        return ans;//返回答案
    }
    int Dp(vector<int>&nums,int target)
    {
        vector<int>dp(target+1);
        dp[0]=1;//根据递归函数的base填的
        for(int i=1;i<=target;i++)
        {
         long long  ans=0;
        for(int j=0;j<nums.size();j++)//这一部分直接抄递归函数
        {
            if(i-nums[j]>=0)//组合数小于0
            {
              ans+=dp[i-nums[j]];
            } 
        }
        dp[i]=ans;
        }
        return dp[target];//根据递归函数的调用确定返回值
    }

};

11. Incrementing subsequences

491. Incremental Subsequence - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The same is the choice of an element selection and non-selection, but this question requires an increase, so when we define the recursive function again, we need to add a parameter to represent the previous element in the array to determine whether this number can be selected and whether it can be selected or not. Deduplication is performed when this number is selected. Take [6, 7, 7, 7] as an example

 Corresponding code:

class Solution {
public:
     vector<int>tmp;
     vector<vector<int>>ans;
    vector<vector<int>> findSubsequences(vector<int>& nums) {
          dfs(nums,0,INT_MIN);
          return ans;
    }
    void dfs(vector<int>&nums,int index,int pre)
    {
        if(index==nums.size())//达到了答案
        {
            if(tmp.size()>=2)//判断是否满足至少有两个数
            {
                ans.push_back(tmp);
            }
            return;
        }

       
        if(nums[index]>=pre)//判断是否满足条件
        {
          tmp.push_back(nums[index]);
          dfs(nums,index+1,nums[index]);
          tmp.pop_back();
        }

        if(nums[index]!=pre)//出重
        {
             dfs(nums,index+1,pre);
        }
    }
};

Guess you like

Origin blog.csdn.net/qq_56999918/article/details/123925989