Woodyiiiiiii / LeetCode

My private record of Leetcode solution

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Leetcode 2597. The Number of Beautiful Subsets

Woodyiiiiiii opened this issue · comments

原题地址: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