Skip to content

Leetcode 2597. The Number of Beautiful Subsets #228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Woodyiiiiiii opened this issue Mar 23, 2023 · 0 comments
Open

Leetcode 2597. The Number of Beautiful Subsets #228

Woodyiiiiiii opened this issue Mar 23, 2023 · 0 comments

Comments

@Woodyiiiiiii
Copy link
Owner

Woodyiiiiiii commented Mar 23, 2023

原题地址:2597. The Number of Beautiful Subsets

参考资料:

这道题一开始没有什么特别思路,卡在不知道如何利用绝对值差为k这一条件。再观察数组长度和元素大小,就知道要用DFS递归,可能要用上记忆化搜索或者状态压缩。首先试着用暴力递归写了一版。

注意:

  • 递归回溯有两种写法,第一种是枚举型,我经常写的这种。另一种是选或不选类型。后者比前者写法较简单些。两者时间复杂度都是O(2^n),因为n最大为20,所以最大也就是1024*1024在10^8以下,所以勉强能AC。
  • 同时注意到空间复杂度,既然在1000以内就要好好利用,不要用Set,而是用固定空间数组,这样更能加快运行时间
class Solution {
    int k;
    int[] nums;
    int[] cnt;
    int ans = 0;

    public int beautifulSubsets(int[] nums, int k) {
        this.k = k;
        this.nums = nums;
        Arrays.sort(this.nums);
        cnt = new int[1001];
        for (int i = 0; i < nums.length; ++i) {
            ++cnt[nums[i]];
            dfs(i);
            --cnt[nums[i]];
        }
        return ans;
    }

    private void dfs(int i) {
        if (i == nums.length) {
            return;
        }
        ++ans;
        for (int j = i + 1; j < nums.length; ++j) {
            if (nums[j] - k >= 1 && cnt[nums[j] - k] != 0) {
                continue;
            }
            ++cnt[nums[j]];
            dfs(j);
            --cnt[nums[j]];
        }
    }
}
class Solution {
    int k;
    int[] nums;
    int[] cnt;
    int ans = 0;

    public int beautifulSubsets(int[] nums, int k) {
        this.k = k;
        this.nums = nums;
        Arrays.sort(this.nums);
        cnt = new int[1001];
        dfs(0);
        return ans;
    }

    private void dfs(int i) {
        if (i == nums.length) {
            return;
        }
        dfs(i + 1);
        if (nums[i] - k >= 1 && cnt[nums[i] - k] != 0) {
            return;
        }
        ++ans;
        ++cnt[nums[i]];
        dfs(i + 1);
        --cnt[nums[i]];
    }
}

这是暴力递归的常规写法,没想到直接过了。赛后看其他人的留言,发现C++/python这样写会TLE,所以这是钻了编译器的空子,侥幸过了:)

那么尝试优化。那就要利用好绝对值之差为k这一条件了。

首先看下能不能用记忆化搜索,或者说缓存。换句话说,要看下有没有重复计算的部分,貌似没头绪。

接着看下能不能用状态压缩/DP,有人写了类似的做法但超时了,可以有空看看。

最后根据[Python] House Robber, O(n) By lee215的做法,利用不想交集合相乘HashMap计算。

首先看到k,可以想到利用%k进行分组,这样保证了每组相互间不会形成绝对值之差为k的subset,每组进行数量相乘就可以求出最终答案。接着,要计算组内能形成满足条件的subset,则利用了198. House Robber题型的思想,对组内数组排序后计算相邻差不为k的个数。

class Solution {
    public int beautifulSubsets(int[] nums, int k) {
        Map<Integer, TreeMap<Integer, Integer>> map = new HashMap<>();
        for (int num : nums) {
            int key = num % k;
            map.putIfAbsent(key, new TreeMap<>());
            map.get(key).put(num, map.get(key).getOrDefault(num, 0) + 1);
        }
        int res = 1;
        for (int key : map.keySet()) {
            int pre = -1, dp0 = 1, dp1 = 0;
            for (int num : map.get(key).keySet()) {
                int v = (int) (Math.pow(2, map.get(key).get(num)) - 1);
                if (num - pre == k) {
                    int t = dp1;
                    dp1 = (dp0 * v);
                    dp0 += t;
                } else {
                    int t = dp1;
                    dp1 = (dp0 + dp1) * v;
                    dp0 += t;
                }
                pre = num;
            }
            res *= (dp0 + dp1);
        }
        return res - 1;
    }
}

这种求subset个数类型的题目,最终利用集合相乘和Hash的方法,最近还有一道类似的题目:2572. Count the Number of Square-Free Subsets

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant