# Leetcode 第55场双周赛题解

# Problem A - 删除一个元素使数组严格递增 (opens new window)

# 方法一:暴力

本题数据范围较小,因此我们可以暴力枚举删除的元素,然后检查剩余的数组是否严格递增。

  • 时间复杂度O(N2)\mathcal{O}(N^2)
  • 空间复杂度O(N)\mathcal{O}(N)
参考代码(Python 3)
class Solution:
    def canBeIncreasing(self, nums: List[int]) -> bool:
        n = len(nums)
        for i in range(n):
            sub = nums[:i] + nums[i + 1:]
            good = True
            for j in range(len(sub) - 1):
                if sub[j + 1] <= sub[j]:
                    good = False
                    break
            if good:
                return True
        return False
1
2
3
4
5
6
7
8
9
10
11
12
13

# 方法二:双指针

最终剩下的数组必然由原数组的一个前缀和一个后缀组合而成,因此我们可以采用双指针的方法:

  • 左指针找出最长的严格递增的前缀;
  • 右指针找出最长的严格递增的后缀。

然后,我们判断这个前缀和这个后缀是否能按照题目要求进行组合。

  • 时间复杂度O(N)\mathcal{O}(N)
  • 空间复杂度O(1)\mathcal{O}(1)
参考代码(C++)
class Solution {
public:
    bool canBeIncreasing(vector<int>& nums) {
        int n = nums.size();
        if (n <= 2)
            return true;
        
        int p = 0;
        while (p + 1 < n && nums[p + 1] > nums[p])
            p++;
        if (p == n - 1)
            return true;
        
        int q = n - 1;
        while (q >= 1 && nums[q - 1] < nums[q])
            q--;
        
        if (p + 2 == q && nums[p] < nums[q])
            return true;
        
        if (p + 1 == q) {
            if (p == 0 || (p >= 1 && nums[p - 1] < nums[q]))
                return true;
            if (q == n - 1 || (q + 1 < n && nums[p] < nums[q + 1]))
                return true;
        }
        
        return false;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Problem B - 删除一个字符串中所有出现的给定子字符串 (opens new window)

# 方法一:模拟

本题数据范围较小,我们可以直接进行模拟,将原字符串中的字符逐个加入,每次加入一个字符,就判断当前结果串的尾部是否与给定字符串相同,若是,则将其删去。

  • 时间复杂度为O(ST)\mathcal{O}(|S||T|)
  • 空间复杂度O(S)\mathcal{O}(|S|)
参考代码(C++)
class Solution {
public:
    string removeOccurrences(string s, string part) {
        string ans;
        for (char c : s) {
            ans.push_back(c);
            if (ans.size() >= part.size() && ans.substr(ans.size() - part.size(), part.size()) == part)
                ans = ans.substr(0, ans.size() - part.size());
        }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12

# 方法二:KMP算法

我们可以使用KMP算法来实现线性复杂度。

首先预处理得到给定字符串对应的π\pi数组(表示以当前位置为结尾,且与字符串的前缀相同的最长字符串的长度),然后逐个处理原始字符串中的字符。每当匹配成功时,回退相应的长度即可。

  • 时间复杂度为O(S+T)\mathcal{O}(|S|+|T|)
  • 空间复杂度O(S+T)\mathcal{O}(|S|+|T|)
参考代码(Python 3)
class Solution:
    def removeOccurrences(self, s: str, part: str) -> str:
        m = len(part)
        pi = [0] * m
        for i in range(1, m):
            j = pi[i - 1]
            while j != 0 and part[i] != part[j]:
                j = pi[j - 1]
            pi[i] = j + 1 if part[i] == part[j] else 0
        
        ans = []
        dp = [0]
        for c in s:
            ans.append(c)
            j = dp[-1]
            while j != 0 and c != part[j]:
                j = pi[j - 1]
            dp.append(j + 1 if c == part[j] else 0)
            if dp[-1] == m:
                dp = dp[:-m]
                ans = ans[:-m]
        
        return ''.join(ans)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Problem C - 最大子序列交替和 (opens new window)

# 方法一:动态规划

我们维护两个值:当前长度为偶数的子序列的最大和、当前长度为奇数的子序列的最大和。

遍历所有元素,对于每个元素,我们都有选取/不选取这两种情况,从而可以更新两个最大值。

最后的结果就是长度为奇数的子序列的最大和(想想为什么不可能是长度为偶数的子序列的最大和?)。

  • 时间复杂度O(N)\mathcal{O}(N)
  • 空间复杂度O(1)\mathcal{O}(1)
参考代码(Python 3)
class Solution:
    def maxAlternatingSum(self, nums: List[int]) -> int:
        odd = int(-1e10)
        even = 0
        for num in nums:
            odd, even = max(odd, even + num), max(even, odd - num)
        return odd
1
2
3
4
5
6
7

# Problem D - 设计电影租借系统 (opens new window)

# 方法一:数据结构+模拟

按照题目要求,选取合适的数据结构进行模拟即可。

  • 使用一个键值为set的哈希表来存储当前拥有每部电影的店铺。
  • 使用一个set来存储当前借出的所有电影。
  • 使用一个map来存储每个店铺中每部电影的租借价格。

对于查询类操作,因为最多查询55个元素,所以可以先将这些元素移除,再重新加入。

最后各操作的时间复杂度为:

  • searchO(logN)\mathcal{O}(\log N)
  • rentO(logN+logM)\mathcal{O}(\log N+\log M)
  • dropO(logN+logM)\mathcal{O}(\log N+\log M)
  • reportO(logM)\mathcal{O}(\log M)

其中NN为店铺的数目,MM为电影的数目。

参考代码(C++)
class MovieRentingSystem {
    int n;
    unordered_map<int, set<pair<int, int>>> has;
    set<tuple<int, int, int>> borrowed;
    map<pair<int, int>, int> query;
    
public:
    MovieRentingSystem(int n, vector<vector<int>>& entries): n(n) {
        for (auto &v : entries) {
            int shop = v[0], movie = v[1], price = v[2];
            query[{shop, movie}] = price;
            has[movie].emplace(price, shop);
        }
    }
    
    vector<int> search(int movie) {
        vector<int> ans;
        for (int i = 0; i < 5 && !has[movie].empty(); ++i) {
            ans.emplace_back(has[movie].begin()->second);
            has[movie].erase(has[movie].begin());
        }
        
        for (int shop : ans)
            has[movie].emplace(query[{shop, movie}], shop);
        
        return ans;
    }
    
    void rent(int shop, int movie) {
        int price = query[{shop, movie}];
        has[movie].erase({price, shop});
        borrowed.emplace(price, shop, movie);
    }
    
    void drop(int shop, int movie) {
        int price = query[{shop, movie}];
        has[movie].emplace(price, shop);
        borrowed.erase({price, shop, movie});
    }
    
    vector<vector<int>> report() {
        vector<vector<int>> ans;
        
        for (int i = 0; i < 5 && !borrowed.empty(); ++i) {
            auto [price, shop, movie] = *borrowed.begin();
            ans.push_back({shop, movie});
            borrowed.erase(borrowed.begin());
        }
        
        for (auto &v : ans) {
            int shop = v[0], movie = v[1];
            int price = query[{shop, movie}];
            borrowed.emplace(price, shop, movie);
        }
        
        return ans;
    }
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59