关于集合的一系列问题

前几天在力扣刚刷了集合的问题,今天笔试就遇到了,还是有点差别,没有做出来,太菜了。。。

首先看一下leetcode 78.子集

问题:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

思路:记原序列中的元素个数为n,序列中的每个元素有两种状态,即在子集中  不在子集中,用1,0分别表示在和不在,

则每一个子集就对应一个长度为n的0/1序列。例如:n = 3, a = {1, 2, 3}

000 {} 0
001 {3} 1
010 {2} 2
011 {2, 3} 3
100 {1} 4
101 {1, 3} 5
110 {1, 2} 6
111 {1, 2, 3} 7

可以看到0/1序列对应的二进制正好从0到2n-1, 枚举mask∈[0, 2n-1],mask的二进制表示是一个 0/1序列,我们可以按照这个 0/1序列在原集合当中取数。当我们枚举完所有mask,就能构造出所有的子集

复杂度分析

时间复杂度:O(n×2n),一共 2n个状态,每种状态需要 O(n)的时间来构造子集

空间复杂度:O(n)。即构造子集使用的临时数组 t的空间代价。

class Solution {
public:
    vector<int> t;
    vector<vector<int>> res;
    vector<vector<int>> subsets(vector<int>& nums) {
        int n=nums.size();
        for(int mask = 0; mask < (1<<n); mask++){
            t.clear();
            for(int i =0; i < n; ++i){
                if(mask & (1<<i)){//包含第i位
                    t.push_back(nums[i]);
                }
            }
            res.push_back(t);
        }
        return res;
    }
};

leetcode 90.子集II

问题:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10

思路:

考虑数组 [1,2,2],选择前两个数,或者第一、三个数,都会得到相同的子集。

也就是说,对于当前选择的数 x,若前面有与其相同的数 y,且没有选择 y,此时包含 x 的子集,必然会出现在包含 y 的所有子集中。

我们可以通过判断这种情况,来避免生成重复的子集。代码实现时,可以先将数组排序;迭代时,若发现没有选择上一个数,且当前数字与上一个数相同,则可以跳过当前生成的子集。

复杂度分析:

时间复杂度:O(n×2n),其中 n 是数组 nums 的长度。排序的时间复杂度为O(nlogn)。一共2n个状态,每种状态需要 O(n)的时间来构造子集,一共需要O(n×2n)的时间来构造子集。由于在渐进意义上 O(nlogn) 小于O(n×2n),故总的时间复杂度为 O(n×2n)。

空间复杂度:O(n)。即构造子集使用的临时数组 t 的空间代价。

class Solution {
public:
    vector<int> t;
    vector<vector<int>> ans;

    vector<vector<int>> subsetsWithDup(vector<int> &nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        for (int mask = 0; mask < (1 << n); ++mask) {
            t.clear();
            bool flag = true;
            for (int i = 0; i < n; ++i) {
                if (mask & (1 << i)) {
                    if (i > 0 && (mask >> (i - 1) & 1) == 0 && nums[i] == nums[i - 1]) {
                        flag = false;
                        break;
                    }
                    t.push_back(nums[i]);
                }
            }
            if (flag) {
                ans.push_back(t);
            }
        }
        return ans;
    }
};

本次题目:

输入字符串s,,s中可能有重复字母,求s有多少不含重复字母的子序列,相同字母不同下标,视为不同的子序列。

示例输入:"aabc"

输出:12

解析:"","a","ab","ac","abc","a","ab","ac","abc","b","bc","c"

思路:首先把字符串遍历一遍,记录下每个字符出现的次数,并且把去除重复的字符放入一个字符数组中,然后与子集方法相同,找字符数组的子集,每枚举到一个子集的时候,判断其中的每一个字符是不是重复字符,如果是则count = count + 重复次数。比如aabc ,对于子集"a",包含字符'a',而‘a’重复的次数为2,故count = count + 2,但是如过没有出现重复,我们就正常+1.

复杂度:

时间复杂度:O(n×2n),其中遍历去重,记录重复字符,时间复杂度为O(n), 一共2n个状态,每种状态需要 O(n)的时间来构造子集,一共需要O(n×2n)的时间来构造子集。由于在渐进意义上 O(n) 小于O(n×2n),故总的时间复杂度为 O(n×2n)。

空间复杂度:O(n)。即构造子集使用的临时数组 t 的空间代价,还有字符数组的代价。

#include<iostream>
#include<vector>
#include<string>
#include<unordered_map>
using namespace std;
int main(){
    string s;
    cin>>s;
    unordered_map<int, int> s_map;
    vector<char> res_str;
    int i = 0;
    while(i < s.size()){
        if(s_map.find(s[i]) ==  s_map.end()){
           res_str.push_back(s[i]);
           s_map[s[i]] = 1;
        }else{
            s_map[s[i]]++;
        }
        i++;
    }
    int level = res_str.size();
    int set_count = 1<<level;
    int count = 0;
    vector<char> t;
    for(int i = 0; i < set_count; ++i){
        t.clear();
        for(int j = 0; j < level; ++j){
            if(i & (1 << j)){
                t.push_back(res_str[j]);
            }
        }
        if(t.size() > 0){
            int sign = 0;
            for(auto a : t ){
                if(s_map[a] > 1){
                    sign++;
                    count += s_map[a] % 20210101;
                }
            }
            if(sign == 0){
                count++;
            }
        } else {
            count++;
        }
    }
    cout<<count;
    return 0;
}
原文地址:https://www.cnblogs.com/doris233/p/14616655.html