# Leetcode 第62场双周赛题解

# Problem A - 将一维数组转变成二维数组 (opens new window)

# 方法一:模拟

按要求操作即可。

  • 时间复杂度O(NM)\mathcal{O}(NM)
  • 空间复杂度O(NM)\mathcal{O}(NM)
参考代码(Python 3)
class Solution:
    def construct2DArray(self, original: List[int], m: int, n: int) -> List[List[int]]:
        if len(original) != m * n:
            return []
        return [original[i * n:(i + 1) * n] for i in range(m)]
1
2
3
4
5

# Problem B - 连接后等于目标字符串的字符串对 (opens new window)

# 方法一:暴力

暴力枚举所有长度符合要求的串即可。

  • 时间复杂度为O(N2S)\mathcal{O}(N^2|S|)
  • 空间复杂度O(S)\mathcal{O}(|S|)
参考代码(C++)
class Solution {
public:
    int numOfPairs(vector<string>& nums, string target) {
        int ans = 0, n = nums.size();
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n; ++j) {
                if (i != j && nums[i].size() + nums[j].size() == target.size() && nums[i] + nums[j] == target)
                    ans++;
            }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12

# 方法二:前后缀

我们可以逐个判断每个字符串是否是target的前缀或后缀,并对后缀长度计数。

然后,我们枚举所有是前缀的字符串,根据哈希表的计数结果就可以知道有多少个与之匹配的后缀。注意这里需要排除掉它自身也是后缀并且长度刚好为target的一半的情形。

  • 时间复杂度为O(NS)\mathcal{O}(N|S|)
  • 空间复杂度O(S)\mathcal{O}(|S|)
参考代码(C++)
class Solution {
public:
    int numOfPairs(vector<string>& nums, string target) {
        int n = nums.size(), t = target.size();
        vector<bool> pre(n), suf(n);
        unordered_map<int, int> suf_map;
        for (int i = 0; i < n; ++i) {
            auto &num = nums[i];
            int m = num.size();
            if (m > target.size())
                continue;
            if (num == target.substr(0, m))
                pre[i] = true;
            if (num == target.substr(t - m, m))
                suf[i] = true, suf_map[m]++;
        }
        
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            if (pre[i]) {
                ans += suf_map[t - nums[i].size()];
                if (suf[i] && nums[i].size() * 2 == t)
                    ans--;
            }
        }
        
        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

# Problem C - 考试的最大困扰度 (opens new window)

# 方法一:贪心

显然,想要连续相邻的T,就应该改变连续的F(未必相邻);想要连续相邻的F,就应该改变连续的T(未必相邻)。所以我们找出TF的位置,分别考虑尽可能获取T和尽可能获取F这两种情形即可。

  • 时间复杂度O(N)\mathcal{O}(N)
  • 空间复杂度O(N)\mathcal{O}(N)
参考代码(C++)
class Solution {
public:
    int maxConsecutiveAnswers(string answerKey, int k) {
        vector<int> T{-1}, F{-1};
        int n = answerKey.size();
        for (int i = 0; i < n; ++i) {
            if (answerKey[i] == 'T')
                T.push_back(i);
            else
                F.push_back(i);
        }
        T.push_back(n), F.push_back(n);
        
        if (k >= T.size() - 2 || k >= F.size() - 2)
            return n;
        
        int ans = 0;
        for (int i = 1; i + k < T.size(); ++i)
            ans = max(ans, T[i + k] - T[i - 1] - 1);
        for (int i = 1; i + k < F.size(); ++i)
            ans = max(ans, F[i + k] - F[i - 1] - 1);
        
        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

# Problem D - 分割数组的最多方案数 (opens new window)

# 方法一:哈希表

不进行改变的结果是容易求得的。

下面考虑改变一次的结果。

对于第ii个元素,将其改变为kk后的数值变化是knums[i]k - nums[i]。问题是,我们并不知道第ii个元素在划分中是属于左边还是右边。

可以发现,第ii个元素有nin - i种在左边的情况,有i1i-1种在右边的情况。而第ii个元素和第i+1i+1个元素只在一种划分中分属两边。这提示我们使用前后缀的思想:从第一个元素开始,考虑n1n-1种当前元素在左边的情况,然后每经过一个位置,进行一次修改。对于当前元素在左边的情况,我们对LR\sum L-\sum R进行计数;反之,则对RL\sum R-\sum L计数。这样我们就可以由delta[nums[i]k]delta[nums[i]-k]得到将当前元素修改为kk后可以得到的划分数。

  • 时间复杂度O(N)\mathcal{O}(N)
  • 空间复杂度O(N)\mathcal{O}(N)
参考代码(C++)
using ll = long long;

class Solution {
public:
    int waysToPartition(vector<int>& nums, int k) {
        int n = nums.size();
        int ans = 0;
        vector<ll> pre(n + 1);
        for (int i = 1; i <= n; ++i)
            pre[i] = pre[i - 1] + nums[i - 1];
        
        // Do not change
        for (int i = 1; i < n; ++i) {
            if (pre[i] * 2 == pre[n])
                ans++;
        }
        
        // Change one
        unordered_map<ll, int> delta;
        for (int i = n; i >= 1; --i) {
            ll l = pre[i - 1];
            ll r = pre[n] - l;
            delta[l - r]++;
        }
                
        for (int i = 1; i <= n; ++i) {
            ll l = pre[i - 1];
            ll r = pre[n] - l;
            if (i > 1)
                delta[r - l]++;
            delta[l - r]--;
            ans = max(ans, delta[nums[i - 1] - k]);
        }
        
        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