LeetCode Contest 54
这次比赛之后,下次再遇到O(n2)的题目,而且n < 1000的时候,一定要用C++!这是泪的教训啊😭
第一题Degree of an Array
在统计每个数字频率的同时,记录这个数字出现的最左和最右位置(l, r)。那么包含这个数字的最短数组的长度为r-l+1。
class Solution(object):
    def findShortestSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        cnt = {}
        f = 0
        for i, n in enumerate(nums):
            if n not in cnt:
                cnt[n] = [1, i, i]
            else:
                c, a, _ = cnt[n]
                cnt[n] = [c+1, a, i]
            f = max(f, cnt[n][0])
        return min(v[2]-v[1]+1 for v in cnt.values() if v[0]==f)
第二题Count Binary Substrings
将字符串按照0和1按组分块,比如"001110011"分成
那么连续的两块能贡献出它们最小长度那么多了符合规定的字符串。比如
"00", "111"贡献了两个class Solution(object):
    def countBinarySubstrings(self, s):
        """
        :type s: str
        :rtype: int
        """
        cnt = map(len, re.split('(0+)', s))
        return sum(min(a, b) for a, b in zip(cnt[:-1], cnt[1:]))
第三题Partition to K Equal Sum Subsets
一个经典的DP问题:给定一个长度为m数组,在O(ms)的时间内判断改数组是否有个子集之和为s。我们可以用dp[i]表示是否有子集之和为i,为了找到这个子集,可以用x = dp[i]表示状态i由状态x加上i-x得到。DP之后,我们可以根据dp的值在O(m)的时间内找到一个和为s的子集。
对于这个问题,我们可以使用上述DP找出和为s的一个子集,去除这个子集中的元素之后得到一个变短的nums。重复上述步骤,如果nums最后能变成空集,则返回True,否则返回False。
注意:这是一个贪心+DP的算法,为了保证算法的正确性,要对nums从大到小排序。
class Solution(object):
    def canPartitionKSubsets(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: bool
        """
        s = sum(nums)
        if s % k != 0:
            return False
        s /= k
        if max(nums) > s:
            return False
        nums.sort(key=lambda x: -x)
        while nums:
            dp = [-1] * (s+1)
            dp[0] = 0
            for n in nums:
                for i in xrange(s, -1, -1):
                    if i + n <= s and dp[i] != -1 and dp[i+n] == -1:
                        dp[i+n] = i
                if dp[s] != -1:
                    break
            if dp[s] == -1:
                return False
            a = s
            while a != 0:
                nums.remove(a-dp[a])
                a = dp[a]
        return True
第四题Falling Squares
第一个想法是线段树,写完之后妥妥的MLE,因为要维护一个长度为108的线段。后来发现正方形的个数最多是1000,所以O(n2)的算法也是可以接受的。但是用Python写还是TLE了,赛后用C++无压力AC了。。。
想法的关键是这样的,对于第i个正方形,扫描之前掉落并与其有重叠部分的正方形高度,以找到第i个正方形最终的高度。
class Solution {
public:
    vector<int> fallingSquares(vector<pair<int, int>>& positions) {
        int n = positions.size();
        vector<int> h(n, 0);
        vector<int> ans(n, 0);
        int c = 0;
        for (int i = 0; i < n; i ++) {
            int t = 0;
            for (int j = 0; j < i; j ++)
                if (overlap(i, j, positions))
                    t = max(t, h[j]);
            t += positions[i].second;
            c = max(c, t);
            ans[i] = c;
            h[i] = t;
        }
        return ans;
    }
private:
    bool overlap(int i, int j, vector<pair<int, int>>& p) {
    int a = p[i].first;
    int b = a+p[i].second;
    int x = p[j].first;
    int y = x + p[j].second;
    return !(a >= y || x >= b);
    }
};
